Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added implementation of everymodule to match newer API. Added in Prom…

…ise and Sequence constructors used in the implementation of the new API.
  • Loading branch information...
commit 32b8fd86a0ba15a1fbca78828be5ed4750801d96 1 parent d9370dc
@bnoguchi authored
View
21 lib/deprecated.js
@@ -68,6 +68,27 @@ everyauth(
)
);
+var fb = module.exports =
+oauthModule.submodule('facebook')
+ // TODO submodule should
+ // set fb.name = 'facebook'
+ .apiHost('https://graph.facebook.com')
+ .setters('scope')
+ .routeStep('authRequest')
+ .uri('/auth/facebook')
+ .step('authRequest')
+ .accepts('req res abc')
+ .returns('*')
+ .get('/auth/facebook')
+ // Should be able to access module
+ // properties from within a step
+ // definition
+ .step('authCallback')
+ .get('/auth/facebook/callback')
+ .step('addToSession')
+ .define( function (sess, auth) {
+
+ })
/** OAuth **/
View
149 lib/everymodule.alt.js
@@ -0,0 +1,149 @@
+var OAuth = require('oauth').OAuth2
+ , url = require('url')
+ , MaterializedSequence = require('./sequence')
+ , clone = require('./utils').clone;
+
+function route (method) {
+ return function (alias) {
+ this._routes[method][alias];
+ this.configurable(alias);
+ this._currentRoute = method + '.' + alias;
+ return this;
+ };
+}
+
+// TODO Add in check for missing step definitions
+// from the set of step declarations
+var everyModule = module.exports = {
+ definit: function (fn) {
+ // Remove any prior `init` that was assigned
+ // directly to the object via definit and not
+ // assigned via prototypal inheritance
+ if (this.hasOwnProperty('init'))
+ delete this.init;
+
+ var super = this.init;
+ // since this.hasOwnProperty('init') is false
+
+ this.init = function () {
+ this.super = super;
+ fn.apply(this, arguments);
+ delete this.super;
+
+ // Do module compilation here, too
+ };
+ }
+ , get: route('get')
+ , post: route('post')
+ , configurable: function (property) {
+ if (property.indexOf(' ') !== -1) {
+ // e.g., property === 'apiHost appId appSecret'
+ var self = this;
+ property.split(/\s+/).forEach( function (_property) {
+ self.configurable(_property);
+ });
+ return this;
+ }
+
+ // Else we have a single property name
+ if (!this[property])
+ this[property] = function (setTo) {
+ var k = '_' + property;
+ if (!arguments.length) return this[k];
+ this[k] = setTo;
+ return this;
+ };
+ return this;
+ }
+ , step: function (name, opts) {
+ // TODO Use opts
+ var steps = this._steps;
+ if (!steps[name]) {
+ steps[name] = {};
+ steps._order.push(name);
+
+ // For configuring what the actual business
+ // logic is:
+ // fb.step('fetchOAuthUser') generates the method
+ // fb.fetchOAuthUser that can be used like
+ // fb.fetchOAuthUser( function (...) {
+ // // Business logic goes here
+ // } );
+ this.configurable(name);
+ }
+ this._currentStep = steps[name];
+ return this;
+ }
+ , accepts: function (input) {
+ var step = this._currentStep;
+ step.accepts = input
+ ? input.split(/\s+/)
+ : null;
+ return this;
+ }
+ , promises: function (output) {
+ var step = this._currentStep;
+ step.promises = output
+ ? output.split(/\s+/)
+ : null;
+ return this;
+ }
+
+ /**
+ * Creates a new submodule using prototypal
+ * inheritance
+ */
+ , submodule: function (name) {
+ var submodule = Object.create(this)
+ , self = this;
+ ['_routes', '_steps'].forEach(
+ function (toClone) {
+ submodule[toClone] = clone(self[toClone]);
+ }
+ );
+ submodule.name = name;
+ return submodule;
+ }
+
+ /**
+ * Decorates the app with the routes required of the
+ * module
+ */
+ , routeApp: function (app) {
+ for (var method in this._routes) {
+ for (var routeAlias in this._routes[method]) {
+ app[method](this[routeAlias](), this.routeHandler(method, routeAlias));
+ }
+ }
+ }
+
+ // Returns the route handler
+ // This is also where a lot of the magic happens
+ , routeHandler: function (method, routeAlias) {
+ var stepsHash = this._steps[method + '.' + routeAlias]
+ // Move orderedSteps to a getter?
+ , orderedSteps = stepsHash._order.map( function (stepName) {
+ return stepsHash[stepName];
+ })
+ , seq = new MaterializedSequence(orderedSteps);
+
+ return seq.routeHandler();
+ // This kicks off a sequence of steps based on a
+ // route
+ // Creates a new chain of promises and exposes the leading promise
+ // to the incoming (req, res) pair from the route handler
+ }
+
+ // _order keeps the order of the steps by name
+ // _steps maps method.routes to step names to step objects
+ // and an ordering of the step names ('_order')
+ // A step object is { accepts: [...], promises: [...] }
+ , _steps: {}
+ , _routes: {
+ get: {}
+ , post: {}
+ }
+}.configurable('init');
+
+everyModule
+ .step('findOrCreateUser');
View
24 lib/facebook.alt.js
@@ -12,26 +12,4 @@ oauthModule.submodule('facebook')
var oauthUser = JSON.parse(data);
p.fulfill(oauthUser);
})
- })
-
-var fb = module.exports =
-oauthModule.submodule('facebook')
- // TODO submodule should
- // set fb.name = 'facebook'
- .apiHost('https://graph.facebook.com')
- .setters('scope')
- .routeStep('authRequest')
- .uri('/auth/facebook')
- .step('authRequest')
- .accepts('req res abc')
- .returns('*')
- .get('/auth/facebook')
- // Should be able to access module
- // properties from within a step
- // definition
- .step('authCallback')
- .get('/auth/facebook/callback')
- .step('addToSession')
- .define( function (sess, auth) {
-
- })
+ });
View
15 lib/oauth.alt.js
@@ -8,7 +8,7 @@ var everyModule = require('./everymodule')
var oauth = module.exports =
everyModule.submodule('oauth')
- .init( function () {
+ .definit( function () {
this.oauth = new OAuth(this.appId(), this.appSecret(), this.apiHost());
})
.configurable('apiHost appId appSecret myHostname redirectPath')
@@ -28,15 +28,18 @@ everyModule.submodule('oauth')
.promises('accessToken refreshToken')
.step('fetchOAuthUser')
.accepts('accessToken refreshToken')
- .promises('user')
- .step('compile')
- .accepts('accessToken refreshToken user')
- .promises('auth')
+ .promises('oauthUser')
.step('getSession')
.accepts('req')
.promises('session')
+ .step('findOrCreateUser')
+ .accepts('session accessToken oauthUser')
+ .promises('user')
+ .step('compile')
+ .accepts('accessToken refreshToken oauthUser user')
+ .promises('auth')
.step('addToSession')
- .accepts('session')
+ .accepts('session auth')
.promises(null)
.step('sendResponse')
.accepts('res')
View
40 lib/promise.js
@@ -0,0 +1,40 @@
+var Promise = module.exports = function (values) {
+ this._callbacks = [];
+ this._errbacks = [];
+ if (arguments.length > 0) {
+ this.fulfill.apply(this, values);
+ }
+};
+
+Promise.prototype = {
+ callback: function (fn, scope) {
+ if (this.values) {
+ return fn.apply(scope, this.values);
+ }
+ this._callback.push([fn, scope]);
+ return this;
+ }
+ , errback: function (fn, scope) {
+ if (this.err) {
+ return fn.call(scope, this.err);
+ }
+ this._errbacks.push([fn, scope]);
+ return this;
+ }
+ , fulfill: function () {
+ var callbacks = this._callbacks;
+ this.values = arguments;
+ for (var i = 0, l = callbacks.length; i < l; i++) {
+ callbacks[i][0].apply(callbacks[i][1], arguments);
+ }
+ return this;
+ }
+ , fail: function (err) {
+ var errbacks = this._errbacks;
+ this.err = err;
+ for (var i = 0, l = errbacks.length; i < l; i++) {
+ errbacks[i][0].call(errbacks[i][1], err);
+ }
+ return this;
+ }
+};
View
100 lib/sequence.js
@@ -0,0 +1,100 @@
+var Promise = require('./promise');
+
+function MaterializedSequence (steps) {
+ // Our sequence state is encapsulated in seq.values
+ this.values = {};
+ this.steps = steps;
+}
+
+MaterializedSequence.prototype = {
+ routeHandler: function () {
+ var seq = this
+ , steps = this.steps
+ , firstStep = steps[0];
+
+ var promise;
+ for (var i = 0, l = steps.length, step; i < l; i++) {
+ step = steps[i];
+ }
+ var promises = this.steps.map( function (step) {
+ var accepts = step.accepts
+ , promises = step.promises
+ , block = step.fn;
+ });
+
+
+ // This kicks off a sequence of steps based on a
+ // route
+ // Creates a new chain of promises and exposes the leading promise
+ // to the incoming (req, res) pair from the route handler
+ return function () {
+ var args = Array.prototype.slice.call(arguments, 0);
+ firstStep.accepts.forEach( function (valName, i) {
+ // Map the incoming arguments to the named parameters
+ // that the first step is expected to accept.
+ seq.values[valName] = args[i];
+ });
+ seq.start();
+
+ };
+ }
+ /**
+ * @param {Object} step
+ * @returns {Promise}
+ */
+ , applyStep: function (step) {
+ var accepts = step.accepts;
+ , promises = step.promises;
+ , block = step.block;
+ // Unwrap values based on step.accepts
+ var args = accepts.reduce( function (args, accept) {
+ args.push(seq.values[accept]);
+ return args;
+ }, []);
+ // Apply the step logic
+ // TODO Set this.module
+ ret = block.apply(this.module, args);
+
+ // Convert return value into a Promise
+ // if it's not yet a Promise
+ if (! (ret instanceof Promise)) {
+ if (Array.isArray(ret))
+ ret = new Promise(ret);
+ else
+ ret = new Promise([ret]);
+ }
+
+ var seq = this;
+ ret.callback( function () {
+ // Store the returned values
+ // in the sequence's state via seq.values
+ var returned = arguments
+ , vals = seq.values;
+ promises.forEach( function (valName, i) {
+ vals[valName] = returned[i];
+ });
+ });
+
+ return ret;
+ }
+ , _bind: function (priorPromise, step) {
+ var nextPromise = new Promise()
+ , seq = this;
+ priorPromise.callback( function () {
+ seq.applyStep(step).callback( function () {
+ nextPromise.fulfill();
+ });
+ });
+ return nextPromise;
+ }
+ , start: function () {
+ var steps = this.steps;
+
+ // Pipe through the steps
+ var priorStepPromise = this.applyStep(steps[0]);
+ for (var i = 1, l = steps.length; i < l; i++) {
+ priorStepPromise = bind(priorStepPromise, steps[i]);
+ }
+ return priorStepPromise;
+ }
+};
Please sign in to comment.
Something went wrong with that request. Please try again.