Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'feature/module'

  • Loading branch information...
commit 446b116c533c925a820c499a583439ee733ed35b 2 parents ded6218 + 4f89ed8
@tj tj authored
View
5 History.md
@@ -1,4 +1,9 @@
+0.1.0 / 2011-04-08
+==================
+
+ * Added `{app,res}.exposeModule(path[, namespace[, name]])`
+
0.0.1 / 2010-01-03
==================
View
21 Readme.md
@@ -16,6 +16,8 @@
## Examples
+### Exposing Objects
+
A common use-case for exposing objects to the client-side would be exposing some properties, perhaps the express configuration. The call to `app.expose(obj)` below defaults to exposing the properties to `express.*`, so for example `express.views`, `express.title`, etc.
app.set('views', __dirname + '/views');
@@ -30,12 +32,11 @@
var math = { add: function(a,b){ return a + b; } };
app.expose(math, 'utils').helpers(math);
-
-
Sometimes you might want to output to a different area, so for this we can pass an additional param "languages" which tells express which buffer to write to, which ends up providing us with the local variable "languages" in our template, where the default is "javascript".
app.expose({ en: 'English', fr: 'French' }, 'express', 'languages');
+### Raw JavaScript
It is also possible to expose "raw" javascript strings.
@@ -45,12 +46,16 @@
app.expose('var some = "variable";', 'head');
+### Exposing Functions
+
Exposing a named function is easy too, simply pass it in with an optional buffer name for placement within a template much like above.
app.expose(function someFunction(){
return 'yay';
}, 'foot');
+### Self-Calling Functions
+
Another alternative is passing an anonymous function, which executes itself, creating a "wrapper" function.
app.expose(function(){
@@ -60,6 +65,18 @@
notify();
});
+### Exposing Entire Modules
+
+ Exposing an entire module as-is is possible as well, this primarily
+ useful when the module relies on internal closures and state.
+
+ The following exposes "color.dark()", "color.light()" etc by default based
+ on the basename of the `path` given, however we pass "utils.color" as a custom namespace.
+
+ app.exposeModule(__dirname + '/color', 'utils.color');
+
+### Request-Level Exposure
+
Finally we can apply all of the above at the request-level as well, below we expose "express.current.user" as `{ name: 'tj' }`, for the specific request only.
app.get('/', function(req, res){
View
7 examples/app.js
@@ -57,6 +57,13 @@ app.expose(function(){
notify();
});
+// expose entire modules to retain their own scope is simple too,
+// simply pass the path, with optional namespace / buffer name.
+
+// the following exposes "color.dark()", "color.light()" etc by default
+// however we pass "utils.color" as a custom namespace.
+app.exposeModule(__dirname + '/color', 'utils.color');
+
app.get('/', function(req, res){
// we might want to expose some user
// as the "express.current.user" global to the client
View
46 lib/express-expose.js
@@ -10,10 +10,13 @@
*/
var express = require('express')
+ , basename = require('path').basename
+ , extname = require('path').extname
, http = require('http')
, res = http.ServerResponse.prototype
, HTTPSServer = express.HTTPSServer
- , HTTPServer = express.HTTPServer;
+ , HTTPServer = express.HTTPServer
+ , fs = require('fs');
/**
* Library version.
@@ -87,9 +90,8 @@ HTTPSServer.prototype.expose = function(obj, namespace, name){
this.expose(';(' + obj + ')();', name);
// buffer object
} else {
- namespace = renderNamespace(namespace);
- this.expose(namespace.string, name);
- this.expose(renderObject(obj, namespace.prop), name);
+ this.expose(renderNamespace(namespace), name);
+ this.expose(renderObject(obj, namespace), name);
this.expose('\n');
}
@@ -97,6 +99,34 @@ HTTPSServer.prototype.expose = function(obj, namespace, name){
};
/**
+ * Expose the module at `path`, with optional `namespace`, defaulting
+ * to the basename, for example `utils/color.js` would expose `colors.dark()`
+ * etc to the client-side.
+ *
+ * @param {String} path
+ * @param {String} namespace
+ * @param {String} name
+ * @return {HTTPServer} for chaining
+ * @api public
+ */
+
+res.exposeModule =
+HTTPServer.prototype.exposeModule =
+HTTPSServer.prototype.exposeModule = function(path, namespace, name){
+ var path = require.resolve(path)
+ , js = fs.readFileSync(path, 'utf8')
+ , namespace = namespace || basename(path, extname(path));
+
+ js = namespace
+ + ' = (function(exports){\n'
+ + js.replace(/^/gm, ' ')
+ + '\n\n return exports;\n})({});';
+
+ this.expose(renderNamespace(namespace));
+ return this.expose(js, name);
+};
+
+/**
* Render the exposed javascript.
*
* @return {String}
@@ -148,17 +178,13 @@ function renderObject(obj, namespace) {
function renderNamespace(str){
var parts = []
, split = str.split('.')
- , len = split.length
- , ret = {};
+ , len = split.length;
- ret.string = str.split('.').map(function(part, i){
+ return str.split('.').map(function(part, i){
parts.push(part);
part = parts.join('.');
- ret.prop = part;
return (i ? '' : 'var ') + part + ' = ' + part + ' || {};';
}).join('\n');
-
- return ret;
}
/**
View
2  package.json
@@ -1,5 +1,5 @@
{ "name": "express-expose"
- , "version": "0.0.1"
+ , "version": "0.1.0"
, "description": "Expose helpers and local variables to the client-side"
, "keywords": ["express"]
, "author": "TJ Holowaychuk <tj@vision-media.ca>"
View
29 test/express-expose.test.js
@@ -133,9 +133,36 @@ module.exports = {
scope.should.not.have.property('add');
},
- 'test res.expose()': function(){
+ 'test app.exposeModule(path)': function(){
+ var app = express.createServer();
+
+ app.exposeModule(__dirname + '/fixtures/color');
+
+ var js = app.exposed()
+ , scope = {};
+
+ vm.runInNewContext(js, scope);
+ scope.color.light('#ffffff').should.be.true;
+ scope.color.light('#000000').should.be.false;
+ },
+
+ 'test app.exposeModule(path, namespace)': function(){
+ var app = express.createServer();
+
+ app.exposeModule(__dirname + '/fixtures/color', 'utils.color');
+
+ var js = app.exposed()
+ , scope = {};
+
+ vm.runInNewContext(js, scope);
+ scope.utils.color.light('#ffffff').should.be.true;
+ scope.utils.color.light('#000000').should.be.false;
+ },
+
+ 'test res.expose(path)': function(){
var app = express.createServer()
, calls = 0;
+
app.set('views', __dirname + '/views');
app.set('view options', { layout: false });
View
45 test/fixtures/color.js
@@ -0,0 +1,45 @@
+
+/**
+ * Parse `rgb` string, ex "#ff0000".
+ */
+
+var parseRGB = exports.parseRGB = function(rgb) {
+ if ('#' == rgb[0]) rgb = rgb.substr(1);
+ var rgb = parseInt(rgb, 16);
+ return {
+ r: (rgb & 0xff0000) >> 16
+ , g: (rgb & 0x00ff00) >> 8
+ , b: (rgb & 0x0000ff)
+ }
+};
+
+/**
+ * Parse `rgb` string and return the lightness
+ * value, aka an average of the max/min components.
+ */
+
+var lightness = exports.lightness = function(rgb){
+ rgb = parseRGB(rgb);
+ var r = rgb.r / 255
+ , g = rgb.g / 255
+ , b = rgb.b / 255
+ , min = Math.min(r,g,b)
+ , max = Math.max(r,g,b);
+ return (min + max) / 2 * 100;
+};
+
+/**
+ * Return true if the `rgb` string is light.
+ */
+
+exports.light = function(rgb) {
+ return lightness(rgb) > 50;
+};
+
+/**
+ * Return true if the `rgb` string is dark.
+ */
+
+exports.dark = function(rgb) {
+ return lightness(rgb) <= 50;
+};
Please sign in to comment.
Something went wrong with that request. Please try again.