<commit_before>/*
 * Geddy JavaScript Web development framework
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var domain = require('domain')
  , fs = require('fs')
  , path = require('path')
  , model = require('model')
  , controller = require('../controller')
  , utils = require('../utils')
  , cwd = process.cwd()
  , errors = require('../response/errors')
  , init = require('../init')
  , helpers = require('../template/helpers')
  , actionHelpers = require('../template/helpers/action')
  , ErrorController = require('../controller/error_controller').ErrorController
  , StaticFileController =
        require('../controller/static_file_controller').StaticFileController
  , controllerInit = require('../controller/init')
  , InFlight = require('../in_flight').InFlight
  , sessions = require('../sessions')
  , usingCoffee // Global variable for CoffeeScript
  , logging = require('./logging')
  , requestHelpers = require('./request_helpers');

// Set up a bunch of aliases
geddy.inFlight = new InFlight();
geddy.inflection = utils.inflection;
geddy.model = model;
geddy.controller = controller;
geddy.utils = utils;
geddy.errors = errors;
geddy.viewHelpers = {};

// Use Geddy logger as the utility logger
utils.log.registerLogger(geddy.log);

var App = function () {

  // Init core -- eventually all init should move here
  // ==================
      _baseInit = function (next) {
        init.init(this, next);
      }

  // Connect to session-store
  // ==================
    , _loadSessionStore = function (next) {
        sessionsConfig = geddy.config.sessions;
        if (sessionsConfig) {
          sessions.createStore(sessionsConfig.store, next);
        }
        else {
          next();
        }
      }

  // Register template-paths
  // ==================
    , _registerTemplatePaths = function (next) {
        var viewsPath = path.normalize('app/views')
          , geddyTemplatesPath = path.join(__dirname, '../template', 'templates');

        // If viewsPath doesn't exist they're running viewless
        if(!utils.file.existsSync(viewsPath)) {
          this.templateRegistry = {};
          next();
        } else {
          var files = utils.file.readdirR(viewsPath)
            , geddyTemplatesFiles = utils.file.readdirR(geddyTemplatesPath)
            , pat = /\.(ejs|jade|hbs|mustache|ms|mu|swig)$/
            , templates = {}
            , file
            , origFile
            , noExtFile
            , fileExt
            , fileBaseName
            , addTemplate
            , createTemplates;

          // Adds a template object to templates
          addTemplate = function (noExtFile, file, origFile, fileExt, fileBaseName) {
            if(!origFile) origFile = noExtFile;

            templates[origFile] = {
                file: file
              , ext: fileExt
              , baseName: fileBaseName
              , baseNamePath: noExtFile
            };
          };

          // Read dir list and create template objects from each file
          createTemplates = function (dir, isGeddy) {
            for (var i = 0; i < dir.length; i++) {
              file = dir[i];
              fileExt = path.extname(file);
              fileBaseName = path.basename(file, fileExt).replace(/\.html$/, '');

              if (isGeddy) {
                origFile = path.normalize('geddy/' + fileBaseName);
              }

              if (pat.test(file)) {
                // Strip .html and extension for easier detecting when rendering
                if (isGeddy) {
                  noExtFile = path.normalize('geddy/' + fileBaseName);
                } else noExtFile = file.replace(/\.html.*$/, '');

                addTemplate(noExtFile, file, origFile, fileExt, fileBaseName);
              }
            }
          };

          // Loop through template files and add it them to registry
          createTemplates(files);

          // Add custom templates from `lib/template/templates`
          createTemplates(geddyTemplatesFiles, true);

          this.templateRegistry = templates;
          next();
        }
      }

  // Run code in the app's config/after_start.js file
  // and run anything else that needs to be run after
  // the server has started listening for requests
  // ================================================
    , _afterStart = function (next) {
        geddy.server.listeners('request');

        // Load socket.io if it's enabled. This gets run after
        // the server starts listening for requests. Socket.io
        // won't work if we start it before.
        if (geddy.config.realtime) {
          geddy.io = utils.file.requireLocal('socket.io').listen(geddy.server, {'log level': 0});
          // add event listeners to all the models
          // set up socket.io emitters for each event

          var events = [
          , 'save'
          , 'update'
          , 'beforeRemove'
          ]

          , registerEventListener = function (model, event) {
            geddy.model[model].on(event, function (d) {
              var data = geddy.mixin({}, d)
                , id;

              event = event.replace('before','').toLowerCase();

              if (typeof data == 'object') {
                data.model = model;
                data.event = event;
                id = data.id;
              }
              else {
                id = data;
              }

              geddy.io.sockets.emit(model+':'+event, data);
              geddy.io.sockets.emit(model+':'+event+':'+id, data);
            });
          }

          for (var i in geddy.model.descriptionRegistry) {
            for (var e in events) {
              registerEventListener(i, events[e]);
            }
          }

        }

        try {
          require(path.join(cwd, 'config', 'after_start'));
        } catch (e) {}
      };

  this.router = null;
  this.modelRegistry = {};
  this.templateRegistry = {};
  this.controllerRegistry = {};

  this.init = function (callback) {
    var self = this
      , items
      , chain;

    items = [
      _baseInit
    // TODO: Migrate all these into _baseInit
    , _loadSessionStore
    , _registerTemplatePaths
    ];

    chain = new utils.async.SimpleAsyncChain(items, this);
    chain.last = function () {
      self.start(callback);
    };
    chain.run();
  };

  this.handleControllerAction = function (controllerInst, reqUrl, method,
          params, accessTime, reqObj, respObj) {
    var initKeys
      , initializer
      , cb;

    // Async setup steps to allow the controller to handle
    // the request
    initKeys = [
      'cookies'
    , 'i18n'
    , 'inFlight'
    , 'parseBody'
    , 'session'
    ];

    // Mix all the shits onto the controller instance
    utils.mixin(controllerInst, {
      app: this
    , url: reqUrl  // Can we dispense with this? It's in the params
    , method: method // And this?
    , params: params
    , accessTime: accessTime
    , request: reqObj
    , response: respObj
    , name: params.controller
    });

    reqObj.controller = controllerInst;
    respObj.controller = controllerInst;

    cb = function () {
      controllerInst._handleAction.call(controllerInst, params.action);
      // TODO Replace this with readable-stream module for 0.8 support
      if (reqObj.req && typeof reqObj.req.read != 'function') {
        reqObj.sync(); // Flush buffered events and begin emitting
      }
    };
    initializer = new utils.async.Initializer(initKeys, cb);

    // Run all the async setup steps
    initKeys.forEach(function (key) {
      controllerInit[key].call(controllerInst, function () {
        initializer.complete(key);
      });
    });
  };

  this.handleStaticFile = function (staticPath, params, reqUrl, reqObj, respObj) {
    var controllerInst;
    // May be a path to a directory, with or without a trailing
    // slash -- any trailing slash has already been stripped by
    // this point
    if (fs.statSync(staticPath).isDirectory()) {
      // TODO: Make the name of any index file configurable
      staticPath = path.join(staticPath, 'index.html');
      if (utils.file.existsSync(staticPath) && fs.statSync(staticPath).isFile()) {
        controllerInst = new StaticFileController(reqObj, respObj, params);
        controllerInst.respond({
          path: staticPath
        });
      }
      // Directory with no index file
      else {
        this.handleNotFound(reqUrl, params, reqObj, respObj);
      }
    }
    // Path to an actual file. Just serve it up
    else if (fs.statSync(staticPath).isFile()) {
      controllerInst = new StaticFileController(reqObj, respObj, params);
      controllerInst.respond({
        path: staticPath
      });
    }
  };

  this.handleMethodNotAllowed = function (method, reqUrl, params, reqObj, respObj,
      nonMethodRoutes) {
    // build a unique list of acceptable methods for this resource
    var acceptableMethods = {}
      , err
      , controllerInst;

    nonMethodRoutes.map(function (params) {
      acceptableMethods[params.method] = true;
    });
    acceptableMethods = Object.keys(acceptableMethods);

    // send a friendly error response
    throw new errors.MethodNotAllowedError(
      method + ' method not allowed. Please consider ' +
      acceptableMethods.join(', ').replace(/,\s(\w+)$/," or $1") +
      ' instead.');
  };

  this.handleNotFound = function (reqUrl, params, reqObj, respObj) {
    throw new errors.NotFoundError(reqUrl + ' not found.');
  };

  this.handleNoMatchedRoute = function (method, reqUrl, params, reqObj, respObj) {
    var staticPath
      , controllerInst
      , nonMethodRoutes;

    // Get the path to the file, decoding the request URI
    staticPath = geddy.config.staticFilePath + decodeURIComponent(reqUrl);
    // Ignore querystring
    staticPath = staticPath.split('?')[0];

    // Static?
    if (utils.file.existsSync(staticPath)) {
      this.handleStaticFile(staticPath, params, reqUrl, reqObj, respObj, params);
    }
    else {
      nonMethodRoutes = this.router.all(reqUrl);

      // Good route, wrong verb -- 405?
      if (nonMethodRoutes.length) {
        this.handleMethodNotAllowed(method, reqUrl, params, reqObj, respObj,
          nonMethodRoutes);
      }
      // Nada, 404
      else {
        this.handleNotFound(reqUrl, params, reqObj, respObj);
      }
    }
  };
  this.handleNoAction = function (params, reqObj, respObj) {
    throw new errors.InternalServerError('No ' + params.action +
        ' action on ' + params.controller + ' controller.');
  };

  this.handleNoController = function (params, reqObj, respObj) {
    throw new errors.InternalServerError('controller ' +
        params.controller + ' not found.');
  };

  this.start = function (callback) {
    var self = this
      , ctors = this.controllerRegistry
      , controllerActionTimers = {};

    // Handle the requests
    // ==================
    geddy.server.addListener('request', function (req, resp) {
      var dmn = domain.create()
        , caught = false
        , badRequestErr
        , controllerInst
        , reqObj
        , respObj;

      // Attempt a nice, high-fi customizable error
      // Only try this once -- if something fails during the
      // rendering process for the error, fall back to a low-fi
      // fool-proof error to display that
      dmn.on('error', function (err) {
        var serverErr
          , controllerInst;

        if (caught) {
          return errors.respond(err, respObj);
        }

        caught = true;

        if (err.statusCode) {
          serverErr = err;
        }
        else {
          serverErr = new errors.InternalServerError(err.message, err.stack);
        }

        try {
          controllerInst = new ErrorController(reqObj, respObj);
          controllerInst.respondWith(serverErr);
        }
        // Catch sync errors in the error-rendering process
        // Respond with a low-fi fool-proof err
        // Async ones will be handled by re-entering this domain
        // on-error handler
        catch(e) {
          errors.respond(e, respObj);
        }
      });

      dmn.add(req);
      dmn.add(resp);

      // Parsing URLs may result in a bad request -- if this happens,
      // throw the error inside the domain code, so we can get a nice,
      // customizable error message
      try {
        reqObj = requestHelpers.enhanceRequest(req);
        respObj = requestHelpers.enhanceResponse(resp);
      }
      catch (err) {
        req.url = '/';
        reqObj = requestHelpers.enhanceRequest(req);
        respObj = requestHelpers.enhanceResponse(resp);
        badRequestErr = new errors.BadRequestError(err.message, err.stack);
        controllerInst = new ErrorController(reqObj, respObj);
        return controllerInst.respondWith(badRequestErr);
      }

      dmn.add(reqObj);
      dmn.add(respObj);

      dmn.run(function () {
        var reqUrl
          , urlParams
          , method
          , accessTime
          , params
          , controllerInst;

        // Parse out some needed request properties
        reqUrl = requestHelpers.normalizeUrl(req);
        urlParams = requestHelpers.getUrlParams(reqUrl);
        method = requestHelpers.getMethod(reqUrl, urlParams, req);
        accessTime = requestHelpers.getAccessTime();

        // Now only for timeout, domains are handling errors
        requestHelpers.initInFlight(reqObj, respObj, method, accessTime);

        // TODO: Allow custom formats
        logging.initRequestLogger(reqUrl, reqObj, respObj, method, accessTime);

        params = requestHelpers.getParams(self.router, reqUrl, method);
        // Route/method combo give us something valid?
        if (params) {

          controllerInst = controller.create(params.controller);
          // Valid controller?
          if (controllerInst) {
            // Enhance the parsed params with URL params
            geddy.mixin(params, urlParams);

            // FIXME: Backward-compat shim for old action-name 'destroy'
            if (params.action == 'destroy' &&
                typeof controllerInst.destroy != 'function') {
              params.action = 'remove';
            }

            if (typeof controllerInst[params.action] == 'function') {
              self.handleControllerAction(controllerInst, reqUrl, method,
                      params, accessTime, reqObj, respObj);
            }
            // No action, 500 error
            else {
              self.handleNoAction(params, reqObj, respObj);
            }
          }
          // No controller, 500 error
          else {
            self.handleNoController(params, reqObj, respObj);
          }
        }
        // Either 405, static, or 404
        else {
          self.handleNoMatchedRoute(method, reqUrl, params, reqObj, respObj);
        }

      });
    });

    _afterStart();
    callback();
  };

};

module.exports.App = App;

<commit_msg>Prevents Error "Converting circular structure to JSON" while sending models to socket<commit_after>/*
 * Geddy JavaScript Web development framework
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var domain = require('domain')
  , fs = require('fs')
  , path = require('path')
  , model = require('model')
  , controller = require('../controller')
  , utils = require('../utils')
  , cwd = process.cwd()
  , errors = require('../response/errors')
  , init = require('../init')
  , helpers = require('../template/helpers')
  , actionHelpers = require('../template/helpers/action')
  , ErrorController = require('../controller/error_controller').ErrorController
  , StaticFileController =
        require('../controller/static_file_controller').StaticFileController
  , controllerInit = require('../controller/init')
  , InFlight = require('../in_flight').InFlight
  , sessions = require('../sessions')
  , usingCoffee // Global variable for CoffeeScript
  , logging = require('./logging')
  , requestHelpers = require('./request_helpers');

// Set up a bunch of aliases
geddy.inFlight = new InFlight();
geddy.inflection = utils.inflection;
geddy.model = model;
geddy.controller = controller;
geddy.utils = utils;
geddy.errors = errors;
geddy.viewHelpers = {};

// Use Geddy logger as the utility logger
utils.log.registerLogger(geddy.log);

var App = function () {

  // Init core -- eventually all init should move here
  // ==================
      _baseInit = function (next) {
        init.init(this, next);
      }

  // Connect to session-store
  // ==================
    , _loadSessionStore = function (next) {
        sessionsConfig = geddy.config.sessions;
        if (sessionsConfig) {
          sessions.createStore(sessionsConfig.store, next);
        }
        else {
          next();
        }
      }

  // Register template-paths
  // ==================
    , _registerTemplatePaths = function (next) {
        var viewsPath = path.normalize('app/views')
          , geddyTemplatesPath = path.join(__dirname, '../template', 'templates');

        // If viewsPath doesn't exist they're running viewless
        if(!utils.file.existsSync(viewsPath)) {
          this.templateRegistry = {};
          next();
        } else {
          var files = utils.file.readdirR(viewsPath)
            , geddyTemplatesFiles = utils.file.readdirR(geddyTemplatesPath)
            , pat = /\.(ejs|jade|hbs|mustache|ms|mu|swig)$/
            , templates = {}
            , file
            , origFile
            , noExtFile
            , fileExt
            , fileBaseName
            , addTemplate
            , createTemplates;

          // Adds a template object to templates
          addTemplate = function (noExtFile, file, origFile, fileExt, fileBaseName) {
            if(!origFile) origFile = noExtFile;

            templates[origFile] = {
                file: file
              , ext: fileExt
              , baseName: fileBaseName
              , baseNamePath: noExtFile
            };
          };

          // Read dir list and create template objects from each file
          createTemplates = function (dir, isGeddy) {
            for (var i = 0; i < dir.length; i++) {
              file = dir[i];
              fileExt = path.extname(file);
              fileBaseName = path.basename(file, fileExt).replace(/\.html$/, '');

              if (isGeddy) {
                origFile = path.normalize('geddy/' + fileBaseName);
              }

              if (pat.test(file)) {
                // Strip .html and extension for easier detecting when rendering
                if (isGeddy) {
                  noExtFile = path.normalize('geddy/' + fileBaseName);
                } else noExtFile = file.replace(/\.html.*$/, '');

                addTemplate(noExtFile, file, origFile, fileExt, fileBaseName);
              }
            }
          };

          // Loop through template files and add it them to registry
          createTemplates(files);

          // Add custom templates from `lib/template/templates`
          createTemplates(geddyTemplatesFiles, true);

          this.templateRegistry = templates;
          next();
        }
      }

  // Run code in the app's config/after_start.js file
  // and run anything else that needs to be run after
  // the server has started listening for requests
  // ================================================
    , _afterStart = function (next) {
        geddy.server.listeners('request');

        // Load socket.io if it's enabled. This gets run after
        // the server starts listening for requests. Socket.io
        // won't work if we start it before.
        if (geddy.config.realtime) {
          geddy.io = utils.file.requireLocal('socket.io').listen(geddy.server, {'log level': 0});
          // add event listeners to all the models
          // set up socket.io emitters for each event

          var events = [
          , 'save'
          , 'update'
          , 'beforeRemove'
          ]

          , registerEventListener = function (model, event) {
            geddy.model[model].on(event, function (d) {
              var data = geddy.mixin({}, d)
                , id;

              event = event.replace('before','').toLowerCase();

              if (typeof data == 'object') {
                data.model = model;
                data.event = event;
                id = data.id;
              }
              else {
                id = data;
              }

              geddy.io.sockets.emit(model+':'+event, data.toObj());
              geddy.io.sockets.emit(model+':'+event+':'+id, data.toObj());
            });
          }

          for (var i in geddy.model.descriptionRegistry) {
            for (var e in events) {
              registerEventListener(i, events[e]);
            }
          }

        }

        try {
          require(path.join(cwd, 'config', 'after_start'));
        } catch (e) {}
      };

  this.router = null;
  this.modelRegistry = {};
  this.templateRegistry = {};
  this.controllerRegistry = {};

  this.init = function (callback) {
    var self = this
      , items
      , chain;

    items = [
      _baseInit
    // TODO: Migrate all these into _baseInit
    , _loadSessionStore
    , _registerTemplatePaths
    ];

    chain = new utils.async.SimpleAsyncChain(items, this);
    chain.last = function () {
      self.start(callback);
    };
    chain.run();
  };

  this.handleControllerAction = function (controllerInst, reqUrl, method,
          params, accessTime, reqObj, respObj) {
    var initKeys
      , initializer
      , cb;

    // Async setup steps to allow the controller to handle
    // the request
    initKeys = [
      'cookies'
    , 'i18n'
    , 'inFlight'
    , 'parseBody'
    , 'session'
    ];

    // Mix all the shits onto the controller instance
    utils.mixin(controllerInst, {
      app: this
    , url: reqUrl  // Can we dispense with this? It's in the params
    , method: method // And this?
    , params: params
    , accessTime: accessTime
    , request: reqObj
    , response: respObj
    , name: params.controller
    });

    reqObj.controller = controllerInst;
    respObj.controller = controllerInst;

    cb = function () {
      controllerInst._handleAction.call(controllerInst, params.action);
      // TODO Replace this with readable-stream module for 0.8 support
      if (reqObj.req && typeof reqObj.req.read != 'function') {
        reqObj.sync(); // Flush buffered events and begin emitting
      }
    };
    initializer = new utils.async.Initializer(initKeys, cb);

    // Run all the async setup steps
    initKeys.forEach(function (key) {
      controllerInit[key].call(controllerInst, function () {
        initializer.complete(key);
      });
    });
  };

  this.handleStaticFile = function (staticPath, params, reqUrl, reqObj, respObj) {
    var controllerInst;
    // May be a path to a directory, with or without a trailing
    // slash -- any trailing slash has already been stripped by
    // this point
    if (fs.statSync(staticPath).isDirectory()) {
      // TODO: Make the name of any index file configurable
      staticPath = path.join(staticPath, 'index.html');
      if (utils.file.existsSync(staticPath) && fs.statSync(staticPath).isFile()) {
        controllerInst = new StaticFileController(reqObj, respObj, params);
        controllerInst.respond({
          path: staticPath
        });
      }
      // Directory with no index file
      else {
        this.handleNotFound(reqUrl, params, reqObj, respObj);
      }
    }
    // Path to an actual file. Just serve it up
    else if (fs.statSync(staticPath).isFile()) {
      controllerInst = new StaticFileController(reqObj, respObj, params);
      controllerInst.respond({
        path: staticPath
      });
    }
  };

  this.handleMethodNotAllowed = function (method, reqUrl, params, reqObj, respObj,
      nonMethodRoutes) {
    // build a unique list of acceptable methods for this resource
    var acceptableMethods = {}
      , err
      , controllerInst;

    nonMethodRoutes.map(function (params) {
      acceptableMethods[params.method] = true;
    });
    acceptableMethods = Object.keys(acceptableMethods);

    // send a friendly error response
    throw new errors.MethodNotAllowedError(
      method + ' method not allowed. Please consider ' +
      acceptableMethods.join(', ').replace(/,\s(\w+)$/," or $1") +
      ' instead.');
  };

  this.handleNotFound = function (reqUrl, params, reqObj, respObj) {
    throw new errors.NotFoundError(reqUrl + ' not found.');
  };

  this.handleNoMatchedRoute = function (method, reqUrl, params, reqObj, respObj) {
    var staticPath
      , controllerInst
      , nonMethodRoutes;

    // Get the path to the file, decoding the request URI
    staticPath = geddy.config.staticFilePath + decodeURIComponent(reqUrl);
    // Ignore querystring
    staticPath = staticPath.split('?')[0];

    // Static?
    if (utils.file.existsSync(staticPath)) {
      this.handleStaticFile(staticPath, params, reqUrl, reqObj, respObj, params);
    }
    else {
      nonMethodRoutes = this.router.all(reqUrl);

      // Good route, wrong verb -- 405?
      if (nonMethodRoutes.length) {
        this.handleMethodNotAllowed(method, reqUrl, params, reqObj, respObj,
          nonMethodRoutes);
      }
      // Nada, 404
      else {
        this.handleNotFound(reqUrl, params, reqObj, respObj);
      }
    }
  };
  this.handleNoAction = function (params, reqObj, respObj) {
    throw new errors.InternalServerError('No ' + params.action +
        ' action on ' + params.controller + ' controller.');
  };

  this.handleNoController = function (params, reqObj, respObj) {
    throw new errors.InternalServerError('controller ' +
        params.controller + ' not found.');
  };

  this.start = function (callback) {
    var self = this
      , ctors = this.controllerRegistry
      , controllerActionTimers = {};

    // Handle the requests
    // ==================
    geddy.server.addListener('request', function (req, resp) {
      var dmn = domain.create()
        , caught = false
        , badRequestErr
        , controllerInst
        , reqObj
        , respObj;

      // Attempt a nice, high-fi customizable error
      // Only try this once -- if something fails during the
      // rendering process for the error, fall back to a low-fi
      // fool-proof error to display that
      dmn.on('error', function (err) {
        var serverErr
          , controllerInst;

        if (caught) {
          return errors.respond(err, respObj);
        }

        caught = true;

        if (err.statusCode) {
          serverErr = err;
        }
        else {
          serverErr = new errors.InternalServerError(err.message, err.stack);
        }

        try {
          controllerInst = new ErrorController(reqObj, respObj);
          controllerInst.respondWith(serverErr);
        }
        // Catch sync errors in the error-rendering process
        // Respond with a low-fi fool-proof err
        // Async ones will be handled by re-entering this domain
        // on-error handler
        catch(e) {
          errors.respond(e, respObj);
        }
      });

      dmn.add(req);
      dmn.add(resp);

      // Parsing URLs may result in a bad request -- if this happens,
      // throw the error inside the domain code, so we can get a nice,
      // customizable error message
      try {
        reqObj = requestHelpers.enhanceRequest(req);
        respObj = requestHelpers.enhanceResponse(resp);
      }
      catch (err) {
        req.url = '/';
        reqObj = requestHelpers.enhanceRequest(req);
        respObj = requestHelpers.enhanceResponse(resp);
        badRequestErr = new errors.BadRequestError(err.message, err.stack);
        controllerInst = new ErrorController(reqObj, respObj);
        return controllerInst.respondWith(badRequestErr);
      }

      dmn.add(reqObj);
      dmn.add(respObj);

      dmn.run(function () {
        var reqUrl
          , urlParams
          , method
          , accessTime
          , params
          , controllerInst;

        // Parse out some needed request properties
        reqUrl = requestHelpers.normalizeUrl(req);
        urlParams = requestHelpers.getUrlParams(reqUrl);
        method = requestHelpers.getMethod(reqUrl, urlParams, req);
        accessTime = requestHelpers.getAccessTime();

        // Now only for timeout, domains are handling errors
        requestHelpers.initInFlight(reqObj, respObj, method, accessTime);

        // TODO: Allow custom formats
        logging.initRequestLogger(reqUrl, reqObj, respObj, method, accessTime);

        params = requestHelpers.getParams(self.router, reqUrl, method);
        // Route/method combo give us something valid?
        if (params) {

          controllerInst = controller.create(params.controller);
          // Valid controller?
          if (controllerInst) {
            // Enhance the parsed params with URL params
            geddy.mixin(params, urlParams);

            // FIXME: Backward-compat shim for old action-name 'destroy'
            if (params.action == 'destroy' &&
                typeof controllerInst.destroy != 'function') {
              params.action = 'remove';
            }

            if (typeof controllerInst[params.action] == 'function') {
              self.handleControllerAction(controllerInst, reqUrl, method,
                      params, accessTime, reqObj, respObj);
            }
            // No action, 500 error
            else {
              self.handleNoAction(params, reqObj, respObj);
            }
          }
          // No controller, 500 error
          else {
            self.handleNoController(params, reqObj, respObj);
          }
        }
        // Either 405, static, or 404
        else {
          self.handleNoMatchedRoute(method, reqUrl, params, reqObj, respObj);
        }

      });
    });

    _afterStart();
    callback();
  };

};

module.exports.App = App;

<|endoftext|>


<commit_before>'use strict';

function iterate(item, path, callback) {
    if (typeof path === 'function') {
        callback = path;
        path = 'var';
        callback(item, path);
    }

    Object.keys(item).forEach(function (key) {
        if (typeof item[key] === 'object') {
            if (Array.isArray(item)) {
                return iterate(item[key], path + '[' + key + ']', callback);
            }
            callback(item, path + '.' + key, key);
            return iterate(item[key], path + '.' + key, callback);
        } else {
            if (Array.isArray(item)) {
                callback(item, path + '[' + key + ']', key);
            } else {
                callback(item, path + '.' + key, key);
            }
        }
    });
}

iterate({
    stores: {
        total: 3,
        offset: 0,
        data: [{
            id: '3605',
            name: 'Fnac Preciados',
            street: 'Calle Preciados, 28',
            zip: '28013',
            city: 'Madrid',
            latitude: 40.419027,
            longitude: -3.705254,
            openingTimes: [{
                date: 1439456166345,
                time: '08:00 - 20:00',
                today: true
            }],
            type: 'store',
            homeStore: false,
            newStore: true
        }, {
            id: '4670',
            name: 'Tattoo Joris & Co. Electric Tattooing',
            street: 'Kerkstraat 153',
            zip: '1017 GG',
            city: 'Amsterdam',
            latitude: 52.363600,
            longitude: 4.888569,
            openingTimes: [{
                date: 1439456166345,
                time: '08:00 - 21:00',
                today: true
            }],
            type: 'store',
            homeStore: true,
            newStore: false
        }]
    },
    filters: {
        data: [{
            type: 'quickfilter',
            items: [{
                id: 'openNow',
                title: 'Open now'
            }, {
                id: 'openSunday',
                title: 'Open on Sunday'
            }]
        }, {
            title: 'Filter on closing time',
            type: 'group',
            items: [{
                id: 'openUntil19',
                title: 'Open until 19:00'
            }, {
                id: 'openUntil20',
                title: 'Open until 20:00'
            }, {
                id: 'openUntil21',
                title: 'Open until 21:00'
            }, {
                id: 'openUntil22',
                title: 'Open until 22:00'
            }]
        }]
    }
}, function (item, path, key) {
    var value = key ? item[key] : item;

    if (Array.isArray(value)) {
        line(path, 'to.be.an(\'array\')');
        line(path, 'to.have.length(' + value.length + ')');
    } else if (typeof value === 'object') {
        line(path, 'to.be.an(\'object\')');
        line(path, 'to.have.keys(\'' + Object.keys(value).join('\', \'') + '\')');
    } else if (typeof value === 'number') {
        line(path, 'to.be.a(\'number\')');
        line(path, 'to.be.eql(' + value + ')');
    } else if (typeof value === 'string') {
        line(path, 'to.be.a(\'string\')');
        line(path, 'to.be.eql(\'' + value.replace('\'', '\\\'') + '\')');
    } else if (typeof value === 'boolean') {
        line(path, 'to.be.a(\'boolean\')');
        line(path, 'to.be.eql(\'' + value + '\')');
    } else {
        line(path, '???')
    }

});

function line(path, rest) {
    console.log('chai.expect(' + path + ').' + rest + ';');
}

exports.assertify = iterate;<commit_msg>feat:made the module actually usable<commit_after>'use strict';

function line(path, rest) {
    console.log('chai.expect(' + path + ').' + rest + ';');
}

function generate(item, path, key) {
    var value = key ? item[key] : item;

    if (Array.isArray(value)) {
        line(path, 'to.be.an(\'array\')');
        line(path, 'to.have.length(' + value.length + ')');
    } else if (typeof value === 'object') {
        line(path, 'to.be.an(\'object\')');
        line(path, 'to.have.keys(\'' + Object.keys(value).join('\', \'') + '\')');
    } else if (typeof value === 'number') {
        line(path, 'to.be.a(\'number\')');
        line(path, 'to.be.eql(' + value + ')');
    } else if (typeof value === 'string') {
        line(path, 'to.be.a(\'string\')');
        line(path, 'to.be.eql(\'' + value.replace('\'', '\\\'') + '\')');
    } else if (typeof value === 'boolean') {
        line(path, 'to.be.a(\'boolean\')');
        line(path, 'to.be.eql(\'' + value + '\')');
    } else {
        line(path, '???')
    }
}

function assertify(item, path) {
    if (!path) {
        path = 'var';
        generate(item, path);
    }

    Object.keys(item).forEach(function (key) {
        if (typeof item[key] === 'object') {
            if (Array.isArray(item)) {
                return assertify(item[key], path + '[' + key + ']');
            }
            generate(item, path + '.' + key, key);
            return assertify(item[key], path + '.' + key);
        } else {
            if (Array.isArray(item)) {
                generate(item, path + '[' + key + ']', key);
            } else {
                generate(item, path + '.' + key, key);
            }
        }
    });
}

module.exports = assertify;<|endoftext|>


<commit_before>define([
// Modules
'jquery', 'lacuna', 'mapPlanet', 'library',
// Buildings
'buildings/planetarycommand'], function(
$, Lacuna, MapPlanet, Library,
PlanetaryCommand) {
    function Building() {
        this.view = function(building) {
            Lacuna.send({
                module: building.url,
                method: 'view',
                params: [
                Lacuna.getSession(), // Session Id
                building.id // Building Id
                ],
                success: function(o) {
                    var tabs = [{
                        name: 'View',
                        content: this.getViewTab(o.result.building)
                    }];
                    // Remove the leading slash.
                    building.type = building.url.replace('/', '');
                    var extraTabs = this.buildings[building.type] ? this.buildings[building.type].getTabs() : [];
                    // Put 'em together.
                    if (extraTabs) {
                        tabs = tabs.concat(extraTabs);
                    }
                    this.panel = Lacuna.Panel.newTabbedPanel({
                        draggable: true,
                        name: building.name + ' ' + building.level,
                        preTabContent: this.getBuildingHeader(building),
                        tabs: tabs
                    });
                    // Now that everything is on the screen, add in all the events.
                    if (o.result.building.downgrade.can) {
                        $('#downgradeButton_' + building.id).on(
                            'click', {
                            building: o.result.building,
                            url: building.url,
                            panel: this.panel
                        },
                        this.downgrade);
                    }
                    if (o.result.building.upgrade.can) {
                        $('#upgradeButton_' + building.id).on(
                            'click', {
                            building: o.result.building,
                            url: building.url,
                            panel: this.panel
                        },
                        this.upgrade);
                    }
                    $('#demolishButton_' + building.id).on(
                        'click', {
                        building: o.result.building,
                        url: building.url,
                        panel: this.panel
                    },
                    this.demolish);
                },
                scope: this
            });
        };
        this.getBuildingHeader = function(building) {
            return Lacuna.Templates.tmpl_building_header({
                background_image: $('#lacuna').css('background-image'),
                assets_url: window.assetsUrl,
                building_image: building.image,
                building_desc: Lacuna.getBuildingDesc(building.url)
            });
        };
        this.getViewTab = function(o) {
            var currentProduction = Lacuna.Templates.tmpl_building_current_production({
                assets_url: window.assetsUrl,
                food_hour: Library.formatNum(o.food_hour),
                ore_hour: Library.formatNum(o.ore_hour),
                water_hour: Library.formatNum(o.water_hour),
                energy_hour: Library.formatNum(o.energy_hour),
                waste_hour: Library.formatNum(o.waste_hour),
                happiness_hour: Library.formatNum(o.happiness_hour),
                building_id: o.id
            });
            var upgradeProduction = Lacuna.Templates.tmpl_building_upgrade_production({
                assets_url: window.assetsUrl,
                up_food_hour: Library.formatNum(o.upgrade.production.food_hour),
                up_ore_hour: Library.formatNum(o.upgrade.production.ore_hour),
                up_water_hour: Library.formatNum(o.upgrade.production.water_hour),
                up_energy_hour: Library.formatNum(o.upgrade.production.energy_hour),
                up_waste_hour: Library.formatNum(o.upgrade.production.waste_hour),
                up_happy_hour: Library.formatNum(o.upgrade.production.happiness_hour),
                up_food_bad: parseInt(o.upgrade.production.food_hour) > parseInt(Lacuna.GameData.Status.body.food_hour),
                up_ore_bad: parseInt(o.upgrade.production.ore_hour) > parseInt(Lacuna.GameData.Status.body.ore_hour),
                up_water_bad: parseInt(o.upgrade.production.water_hour) > parseInt(Lacuna.GameData.Status.body.water_hour),
                up_energy_bad: parseInt(o.upgrade.production.energy_hour) > parseInt(Lacuna.GameData.Status.body.energy_hour),
                up_happy_bad: parseInt(o.upgrade.production.happiness_hour) > parseInt(Lacuna.GameData.Status.body.happiness_hour),
                building_id: o.id,
                to_level: parseInt(o.level) - 1,
                downgrade_reason: o.downgrade.reason[1],
                can_downgrade: o.downgrade.can
            });
            var upgradeCost = Lacuna.Templates.tmpl_building_upgrade_cost({
                assets_url: window.assetsUrl,
                up_food_cost: Library.formatNum(o.upgrade.cost.food),
                up_ore_cost: Library.formatNum(o.upgrade.cost.ore),
                up_water_cost: Library.formatNum(o.upgrade.cost.water),
                up_energy_cost: Library.formatNum(o.upgrade.cost.energy),
                up_waste_cost: Library.formatNum(o.upgrade.cost.waste),
                up_time_cost: Library.formatNum(o.upgrade.cost.time) || 15,
                up_food_bad: parseInt(o.upgrade.cost.food) > parseInt(Lacuna.GameData.Status.body.food_storage),
                up_ore_bad: parseInt(o.upgrade.cost.ore) > parseInt(Lacuna.GameData.Status.body.ore_storage),
                up_water_bad: parseInt(o.upgrade.cost.water) > parseInt(Lacuna.GameData.Status.body.water_storage),
                up_energy_bad: parseInt(o.upgrade.cost.energy) > parseInt(Lacuna.GameData.Status.body.energy_storage),
                up_waste_bad: parseInt(o.upgrade.cost.waste) > parseInt(Lacuna.GameData.Status.body.waste_storage),
                building_id: o.id,
                to_level: parseInt(o.level) + 1,
                upgrade_reason: o.upgrade.reason[1],
                can_upgrade: o.upgrade.can
            });
            return [
            currentProduction,
            upgradeProduction,
            upgradeCost].join('');
        };
        this.upgrade = function(e) {
            // TODO: As per the current Web Client there is a popup
            // which warns users about accidentally upgrading a
            // building and using Halls. I do not like this
            // aproach to the situation. Need to find a better way
            // to show the warning but not bother the more experienced
            // players with extra clicking.
            Lacuna.send({
                module: e.data.url,
                method: 'upgrade',
                params: [
                Lacuna.getSession(), // Session Id
                e.data.building.id],
                success: function(o) {
                    // Close the panel.
                    e.data.panel.close(function() {
                        // Refresh planet.
                        Lacuna.MapPlanet.renderPlanet();
                    });
                }
            });
        };
        this.downgrade = function(e) {
            Lacuna.confirm('Are you sure you want to downgrade your ' + e.data.building.name +
                ' to level ' + (parseInt(e.data.building.level) - 1) + '?',
            undefined, function(response) {
                // Once the user has confirmed that they actually
                // want to downgrade the building, do it!
                if (response) {
                    Lacuna.send({
                        module: e.data.url,
                        method: 'downgrade',
                        params: [
                        Lacuna.getSession(), // Session Id
                        e.data.building.id // Building Id
                        ],
                        success: function(o) {
                            // Close the panel.
                            e.data.panel.close(function() {
                                // Refresh planet.
                                MapPlanet.renderPlanet();
                            });
                        }
                    });
                }
            });
        };
        this.demolish = function(e) {
            Lacuna.confirm(
                'Are you sure you want to demolish your ' + e.data.building.name + '?',
            undefined, function(response) {
                if (response) {
                    Lacuna.send({
                        module: e.data.url,
                        method: 'demolish',
                        params: [
                        Lacuna.getSession(), // Session Id
                        e.data.building.id // Building Id
                        ],
                        success: function(o) {
                            // Close the panel.
                            e.data.panel.close(function() {
                                // Refresh planet.
                                MapPlanet.renderPlanet();
                            });
                        }
                    });
                }
            });
        };
        this.buildings = {
            'planetarycommand': PlanetaryCommand
        };
    }
    return new Building();
});<commit_msg>Fixing bugs: PCC funk, Glyph building freezing.<commit_after>define([
// Modules
'jquery', 'lacuna', 'mapPlanet', 'library',
// Buildings
'buildings/planetaryCommand'], function(
$, Lacuna, MapPlanet, Library,
PlanetaryCommand) {
    function Building() {
        this.view = function(building) {
            Lacuna.send({
                module: building.url,
                method: 'view',
                params: [
                Lacuna.getSession(), // Session Id
                building.id // Building Id
                ],
                success: function(o) {
                    var tabs = [{
                        name: 'View',
                        content: this.getViewTab(o.result.building)
                    }];
                    // Remove the leading slash.
                    building.type = building.url.replace('/', '');
                    var extraTabs = this.buildings[building.type] ? this.buildings[building.type].getTabs() : [];
                    // Put 'em together.
                    if (extraTabs) {
                        tabs = tabs.concat(extraTabs);
                    }
                    this.panel = Lacuna.Panel.newTabbedPanel({
                        draggable: true,
                        name: building.name + ' ' + building.level,
                        preTabContent: this.getBuildingHeader(building),
                        tabs: tabs
                    });
                    // Now that everything is on the screen, add in all the events.
                    if (o.result.building.downgrade.can) {
                        $('#downgradeButton_' + building.id).on(
                            'click', {
                            building: o.result.building,
                            url: building.url,
                            panel: this.panel
                        },
                        this.downgrade);
                    }
                    if (o.result.building.upgrade.can) {
                        $('#upgradeButton_' + building.id).on(
                            'click', {
                            building: o.result.building,
                            url: building.url,
                            panel: this.panel
                        },
                        this.upgrade);
                    }
                    $('#demolishButton_' + building.id).on(
                        'click', {
                        building: o.result.building,
                        url: building.url,
                        panel: this.panel
                    },
                    this.demolish);
                },
                scope: this
            });
        };
        this.getBuildingHeader = function(building) {
            return Lacuna.Templates.tmpl_building_header({
                background_image: $('#lacuna').css('background-image'),
                assets_url: window.assetsUrl,
                building_image: building.image,
                building_desc: Lacuna.getBuildingDesc(building.url)
            });
        };
        this.getViewTab = function(o) {
            var currentProduction = Lacuna.Templates.tmpl_building_current_production({
                assets_url: window.assetsUrl,
                food_hour: Library.formatNum(o.food_hour),
                ore_hour: Library.formatNum(o.ore_hour),
                water_hour: Library.formatNum(o.water_hour),
                energy_hour: Library.formatNum(o.energy_hour),
                waste_hour: Library.formatNum(o.waste_hour),
                happiness_hour: Library.formatNum(o.happiness_hour),
                building_id: o.id
            });
            var upgradeProduction = Lacuna.Templates.tmpl_building_upgrade_production({
                assets_url: window.assetsUrl,
                up_food_hour: Library.formatNum(o.upgrade.production.food_hour),
                up_ore_hour: Library.formatNum(o.upgrade.production.ore_hour),
                up_water_hour: Library.formatNum(o.upgrade.production.water_hour),
                up_energy_hour: Library.formatNum(o.upgrade.production.energy_hour),
                up_waste_hour: Library.formatNum(o.upgrade.production.waste_hour),
                up_happy_hour: Library.formatNum(o.upgrade.production.happiness_hour),
                up_food_bad: parseInt(o.upgrade.production.food_hour) > parseInt(Lacuna.GameData.Status.body.food_hour),
                up_ore_bad: parseInt(o.upgrade.production.ore_hour) > parseInt(Lacuna.GameData.Status.body.ore_hour),
                up_water_bad: parseInt(o.upgrade.production.water_hour) > parseInt(Lacuna.GameData.Status.body.water_hour),
                up_energy_bad: parseInt(o.upgrade.production.energy_hour) > parseInt(Lacuna.GameData.Status.body.energy_hour),
                up_happy_bad: parseInt(o.upgrade.production.happiness_hour) > parseInt(Lacuna.GameData.Status.body.happiness_hour),
                building_id: o.id,
                to_level: parseInt(o.level) - 1,
                downgrade_reason: o.downgrade.reason[1],
                can_downgrade: o.downgrade.can
            });
            var upgradeCost = Lacuna.Templates.tmpl_building_upgrade_cost({
                assets_url: window.assetsUrl,
                up_food_cost: Library.formatNum(o.upgrade.cost.food || 0),
                up_ore_cost: Library.formatNum(o.upgrade.cost.ore || 0),
                up_water_cost: Library.formatNum(o.upgrade.cost.water || 0),
                up_energy_cost: Library.formatNum(o.upgrade.cost.energy || 0),
                up_waste_cost: Library.formatNum(o.upgrade.cost.waste || 0),
                up_time_cost: Library.formatNum(o.upgrade.cost.time || 15),
                up_food_bad: parseInt(o.upgrade.cost.food) > parseInt(Lacuna.GameData.Status.body.food_storage),
                up_ore_bad: parseInt(o.upgrade.cost.ore) > parseInt(Lacuna.GameData.Status.body.ore_storage),
                up_water_bad: parseInt(o.upgrade.cost.water) > parseInt(Lacuna.GameData.Status.body.water_storage),
                up_energy_bad: parseInt(o.upgrade.cost.energy) > parseInt(Lacuna.GameData.Status.body.energy_storage),
                up_waste_bad: parseInt(o.upgrade.cost.waste) > parseInt(Lacuna.GameData.Status.body.waste_storage),
                building_id: o.id,
                to_level: parseInt(o.level) + 1,
                upgrade_reason: o.upgrade.reason[1],
                can_upgrade: o.upgrade.can
            });
            return [
            currentProduction,
            upgradeProduction,
            upgradeCost].join('');
        };
        this.upgrade = function(e) {
            // TODO: As per the current Web Client there is a popup
            // which warns users about accidentally upgrading a
            // building and using Halls. I do not like this
            // aproach to the situation. Need to find a better way
            // to show the warning but not bother the more experienced
            // players with extra clicking.
            Lacuna.send({
                module: e.data.url,
                method: 'upgrade',
                params: [
                Lacuna.getSession(), // Session Id
                e.data.building.id],
                success: function(o) {
                    // Close the panel.
                    e.data.panel.close(function() {
                        // Refresh planet.
                        Lacuna.MapPlanet.renderPlanet();
                    });
                }
            });
        };
        this.downgrade = function(e) {
            Lacuna.confirm('Are you sure you want to downgrade your ' + e.data.building.name +
                ' to level ' + (parseInt(e.data.building.level) - 1) + '?',
            undefined, function(response) {
                // Once the user has confirmed that they actually
                // want to downgrade the building, do it!
                if (response) {
                    Lacuna.send({
                        module: e.data.url,
                        method: 'downgrade',
                        params: [
                        Lacuna.getSession(), // Session Id
                        e.data.building.id // Building Id
                        ],
                        success: function(o) {
                            // Close the panel.
                            e.data.panel.close(function() {
                                // Refresh planet.
                                MapPlanet.renderPlanet();
                            });
                        }
                    });
                }
            });
        };
        this.demolish = function(e) {
            Lacuna.confirm(
                'Are you sure you want to demolish your ' + e.data.building.name + '?',
            undefined, function(response) {
                if (response) {
                    Lacuna.send({
                        module: e.data.url,
                        method: 'demolish',
                        params: [
                        Lacuna.getSession(), // Session Id
                        e.data.building.id // Building Id
                        ],
                        success: function(o) {
                            // Close the panel.
                            e.data.panel.close(function() {
                                // Refresh planet.
                                MapPlanet.renderPlanet();
                            });
                        }
                    });
                }
            });
        };
        this.buildings = {
            'planetarycommand': PlanetaryCommand
        };
    }
    return new Building();
});<|endoftext|>


<commit_before>import {exec} from 'child_process';
import path from 'path';
import promisify from 'es6-promisify';
import npm from 'npm';
import {gt} from 'semver';
import {log} from 'gulp-util';

import localNpm from './local-npm-helper';

const npmLoad = promisify(npm.load);

async function filterPackages(packageInfos, packageInfo) {
  const {name, version: localVersion, dir} = packageInfo;

  try {
    const publishedVersion = (await promisify(exec)(`npm show ${name} version`)).trim();

    if (gt(localVersion, publishedVersion)) {
      log('Publishing: ', name, dir);
      (await packageInfos).push(packageInfo);
    } else {
      log('Skipping: ', name, dir);
    }
  } catch (e) {
    if (e.message.match(/npm show/)) {
      log(`Warning: ${name} is not published`);
    } else {
      console.error(e.stack);
    }
  }

  return packageInfos;
}

export function getPackageInfo(file) {
  const {name, version} = JSON.parse(file.contents.toString());

  return {
    name: name,
    version: version,
    dir: path.dirname(file.path)
  };
}

export async function infoForUpdatedPackages(files) {
  const packageInfos = files.map(getPackageInfo);

  return await packageInfos.reduce(filterPackages, []);
}

export function publishPackages(registry) {
  return async (packageInfos) => {
    await npmLoad({});
    const npmPublish = promisify(npm.commands.publish);
    const npmOwner = promisify(npm.commands.owner);
    if (registry) {
      npm.config.set('registry', registry); //
      if (npm.config.get('registry') !== registry) { //
        const e = new Error('Must be pointing at private npm to test locally!');
        console.error(e);
        callback(e);
        return;
      }
    }
    for (const index in packageInfos) {
      let packageInfo = packageInfos[index];
      if (packageInfo && packageInfo.name) {

        await npmPublish([packageInfo.dir]);

        if (!registry) { //sinopia doesn't seem to support maintainers
          const owners = ['charleshansen', 'atomanyih', 'kennyw1019', 'd-reinhold', 'aberkovec'];
          for (const owner of owners) {
            await npmOwner(['add', owner, packageInfo.name]);
          }
        }
      } else {
        log('Not a valid package', packageInfo);
      }
    }
  };
}

export function publishFakePackages() {
  return publishPackages(localNpm.registryUrl);
}
<commit_msg>chore(publishing) Add Elena to publishing<commit_after>import {exec} from 'child_process';
import path from 'path';
import promisify from 'es6-promisify';
import npm from 'npm';
import {gt} from 'semver';
import {log} from 'gulp-util';

import localNpm from './local-npm-helper';

const npmLoad = promisify(npm.load);

async function filterPackages(packageInfos, packageInfo) {
  const {name, version: localVersion, dir} = packageInfo;

  try {
    const publishedVersion = (await promisify(exec)(`npm show ${name} version`)).trim();

    if (gt(localVersion, publishedVersion)) {
      log('Publishing: ', name, dir);
      (await packageInfos).push(packageInfo);
    } else {
      log('Skipping: ', name, dir);
    }
  } catch (e) {
    if (e.message.match(/npm show/)) {
      log(`Warning: ${name} is not published`);
    } else {
      console.error(e.stack);
    }
  }

  return packageInfos;
}

export function getPackageInfo(file) {
  const {name, version} = JSON.parse(file.contents.toString());

  return {
    name: name,
    version: version,
    dir: path.dirname(file.path)
  };
}

export async function infoForUpdatedPackages(files) {
  const packageInfos = files.map(getPackageInfo);

  return await packageInfos.reduce(filterPackages, []);
}

export function publishPackages(registry) {
  return async (packageInfos) => {
    await npmLoad({});
    const npmPublish = promisify(npm.commands.publish);
    const npmOwner = promisify(npm.commands.owner);
    if (registry) {
      npm.config.set('registry', registry); //
      if (npm.config.get('registry') !== registry) { //
        const e = new Error('Must be pointing at private npm to test locally!');
        console.error(e);
        callback(e);
        return;
      }
    }
    for (const index in packageInfos) {
      let packageInfo = packageInfos[index];
      if (packageInfo && packageInfo.name) {

        await npmPublish([packageInfo.dir]);

        if (!registry) { //sinopia doesn't seem to support maintainers
          const owners = ['charleshansen', 'atomanyih', 'kennyw1019', 'd-reinhold', 'aberkovec', 'esharma'];
          for (const owner of owners) {
            await npmOwner(['add', owner, packageInfo.name]);
          }
        }
      } else {
        log('Not a valid package', packageInfo);
      }
    }
  };
}

export function publishFakePackages() {
  return publishPackages(localNpm.registryUrl);
}
<|endoftext|>


<commit_before>// Represents a Beanstalkd session that works similar to an IronMQ client.
//
// Since GET requests block, need separate sessions for pushing and processing,
// and each is also setup differently.
//
// Underneath there is a Fivebeans client that gets connected and automatically
// reconnected after failure (plus some error handling missing from Fivebeans).
//
// To use the underlying client, call the method `request`.

'use strict';
const Fivebeans = require('fivebeans');
const debug     = require('debug')('ironium:beanstalk');
const ms        = require('ms');

// How long to wait establishing a new connection.
const CONNECT_TIMEOUT       = ms('5s');

// Error talking to queue server, typically transient
class QueueError extends Error {

  constructor(error) {
    super();
    this.message = error.message || error;
    this.name    = error.name    || this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }

}

module.exports = class Beanstalk {

  // Construct a new session.
  //
  // id      - Session id, used for error reporting
  // config  - Queue server configuration, used to establish connection
  // setup   - The setup function, see below
  //
  // Beanstalkd requires setup to either use or watch a tube, depending on the
  // session.  The setup function is provided when creating the session, and is
  // called after the connection has been established.
  //
  // The setup function is called with two arguments:
  // client   - Fivebeans client being setup
  // callback - Call with error or nothing
  constructor(config, id, setup) {
    this.config  = config;
    this.id      = id;
    this.setup   = setup;

    this._connectPromise = null;
  }

  // Make a request to the server.
  //
  // command  - The command (e.g. put, get, destroy)
  // args     - Zero or more arguments, depends on command
  //
  // Returns a promise that resolves to single value, or array, depending on
  // command.
  //
  // This is a simple wrapper around the Fivebeans client with additional error
  // handling.
  request(command) {
    const args = Array.from(arguments).slice(1);

    // Ask for connection, we get a promise that resolves into a client.
    // We return a new promise that resolves to the output of the request.
    return this.connect()
      .then(client => {

        return new Promise((resolve, reject) => {
          // Processing (continuously or not) know to ignore the TIMED_OUT
          // and CLOSED errors.

          // When Fivebeans client executes a command, it doesn't monitor for
          // connection close/error, so we need to catch these events ourselves
          // and reject the promise.
          const onConnectionEnded = (error) => {
            reject(new QueueError(error || 'CLOSED'));
            debug('%s: %s => %s', this.id, command, error || 'CLOSED');
          };

          client.once('close', onConnectionEnded);
          client.once('error', onConnectionEnded);

          const self = this;
          debug('%s: $ %s %j', this.id, command, args);

          function callback(error) {
            const results = Array.from(arguments).slice(1);

            // This may never get called
            client.removeListener('close', onConnectionEnded);
            client.removeListener('error', onConnectionEnded);

            if (error) {
              debug('%s: %s => %s', self.id, command, error);
              reject(new QueueError(error));
            } else if (results.length > 1) {
              debug('%s: %s => %j', self.id, command, results);
              resolve(results);
            } else {
              debug('%s: %s => %j', self.id, command, results[0]);
              resolve(results[0]);
            }
          }
          client[command].apply(client, args.concat(callback));
        });

      });
  }

  // Called to establish a new connection, or use existing connections.
  // Resolves to a FiveBeans client.
  connect() {
    // This may be called multiple times, we only want to establish the
    // connection once, so we reuse the same promise
    if (this._connectPromise)
      return this._connectPromise;

    let connectPromise;

    const onClose = () => {
      // If client connection closed/end, discard the promise
      if (this._connectPromise === connectPromise)
        this._connectPromise = null;
    };

    connectPromise = this._connect(onClose)
      .catch(error => {
        onClose();
        throw new QueueError(error);
      });
    this._connectPromise = connectPromise;
    return connectPromise;
  }

  _connect(onClosed) {
    return Promise.resolve()
      .then(() => {

        // This is the Fivebeans client is essentially a session.
        const config  = this.config;
        const client  = new Fivebeans.client(config.host, config.port); // eslint-disable-line new-cap
        // For when we have a lot of writers contending to push to the same queue.
        client.setMaxListeners(0);

        // Once we established a connection, need to monitor for errors of when
        // server decides to close it
        client.once('error', error => {
          onClosed();
          debug('Client error in queue %s: %s', this.id, error.toString());
        });

        // This promise resolves when client connects.
        const establishConnection = new Promise(function(resolve, reject) {
          // Nothing happens until we start the connection.  Must wait for
          // connection event before we can send anything down the stream.
          client.connect();

          client.on('connect', () => resolve());
          client.on('error', reject);
          client.on('close', function() {
            reject(new QueueError('CLOSED'));
          });

          // Make sure we timeout on slow connections
          setTimeout(function() {
            reject(new Error('Timeout waiting for connection'));
          }, CONNECT_TIMEOUT);
        });

        return establishConnection
          .then(() => {

            // Watch for end and switch to new session
            client.stream.once('end', () => {
              onClosed();
              debug('Connection closed for %s', this.id);
            });

            // Put/reserve clients have different setup requirements, this are handled by
            // an externally supplied method.
            return this.setup(client);
          })
          .then(() => client);

      });
  }

  del(messageID, options, callback) {
    return this.request('destroy', messageID)
      .then(() => callback(), callback);
  }

  msg_release(messageID, reservationID, options, callback) { // eslint-disable-line camelcase
    const priority = 0;
    return this.request('release', messageID, priority, options.delay)
      .then(() => callback(), callback);
  }

  peek(options, callback) {
    const peekReady   = this.request('peek_ready').then(result => result[0]).catch(() => null);
    const peekDelayed = this.request('peek_delayed').then(result => result[0]).catch(() => null);
    return Promise.all([ peekReady, peekDelayed ])
      .then(function(ids) {
        const notNull = ids.filter(id => id);
        callback(null, notNull);
      })
      .catch(callback);
  }

  post(messageOrMessages, callback) {
    const messages = Array.isArray(messageOrMessages) ? messageOrMessages : [messageOrMessages];
    const priority = 0;
    const timeout  = ms('10m');
    const promises = messages.map(message => {
      return this.request('put', priority, message.delay, timeout, message.body);
    });
    return Promise.all(promises)
      .then(ids => callback(null, ids), callback);
  }

  reserve(options, callback) {
    return this.request('reserve_with_timeout', options.wait || 0)
      .then(function(job) {
        const id              = job[0];
        const body            = job[1];
        const reservationID   = id;
        const reservedCount   = 1;

        const message           = { id, body, reservedCount, reservationID };
        callback(null, message);
      })
      .catch(function(error) {
        if (error.message === 'TIMED_OUT')
          callback(null, null);
        else
          callback(error);
      });
  }

  clear(callback) {
    return peekAndDelete(this)
      .then(() => callback(), callback);

    function peekAndDelete(client) {
      return new Promise((resolve, reject) => {
        client.peek({}, (error, ids) => {
          if (error && /not.found/i.test(error))
            resolve();
          else if (error)
            reject(error);
          else if (ids.length) {
            const deleteEach  = ids.map(id => client.request('destroy', id));
            const deleteAll   = Promise.all(deleteEach);
            const andRepeat   = deleteAll.then(() => peekAndDelete(client));
            resolve(andRepeat);
          } else
            resolve();
        });
      });
    }
  }

};

<commit_msg>Ignore this let.<commit_after>// Represents a Beanstalkd session that works similar to an IronMQ client.
//
// Since GET requests block, need separate sessions for pushing and processing,
// and each is also setup differently.
//
// Underneath there is a Fivebeans client that gets connected and automatically
// reconnected after failure (plus some error handling missing from Fivebeans).
//
// To use the underlying client, call the method `request`.

'use strict';
const Fivebeans = require('fivebeans');
const debug     = require('debug')('ironium:beanstalk');
const ms        = require('ms');

// How long to wait establishing a new connection.
const CONNECT_TIMEOUT       = ms('5s');

// Error talking to queue server, typically transient
class QueueError extends Error {

  constructor(error) {
    super();
    this.message = error.message || error;
    this.name    = error.name    || this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }

}

module.exports = class Beanstalk {

  // Construct a new session.
  //
  // id      - Session id, used for error reporting
  // config  - Queue server configuration, used to establish connection
  // setup   - The setup function, see below
  //
  // Beanstalkd requires setup to either use or watch a tube, depending on the
  // session.  The setup function is provided when creating the session, and is
  // called after the connection has been established.
  //
  // The setup function is called with two arguments:
  // client   - Fivebeans client being setup
  // callback - Call with error or nothing
  constructor(config, id, setup) {
    this.config  = config;
    this.id      = id;
    this.setup   = setup;

    this._connectPromise = null;
  }

  // Make a request to the server.
  //
  // command  - The command (e.g. put, get, destroy)
  // args     - Zero or more arguments, depends on command
  //
  // Returns a promise that resolves to single value, or array, depending on
  // command.
  //
  // This is a simple wrapper around the Fivebeans client with additional error
  // handling.
  request(command) {
    const args = Array.from(arguments).slice(1);

    // Ask for connection, we get a promise that resolves into a client.
    // We return a new promise that resolves to the output of the request.
    return this.connect()
      .then(client => {

        return new Promise((resolve, reject) => {
          // Processing (continuously or not) know to ignore the TIMED_OUT
          // and CLOSED errors.

          // When Fivebeans client executes a command, it doesn't monitor for
          // connection close/error, so we need to catch these events ourselves
          // and reject the promise.
          const onConnectionEnded = (error) => {
            reject(new QueueError(error || 'CLOSED'));
            debug('%s: %s => %s', this.id, command, error || 'CLOSED');
          };

          client.once('close', onConnectionEnded);
          client.once('error', onConnectionEnded);

          const self = this;
          debug('%s: $ %s %j', this.id, command, args);

          function callback(error) {
            const results = Array.from(arguments).slice(1);

            // This may never get called
            client.removeListener('close', onConnectionEnded);
            client.removeListener('error', onConnectionEnded);

            if (error) {
              debug('%s: %s => %s', self.id, command, error);
              reject(new QueueError(error));
            } else if (results.length > 1) {
              debug('%s: %s => %j', self.id, command, results);
              resolve(results);
            } else {
              debug('%s: %s => %j', self.id, command, results[0]);
              resolve(results[0]);
            }
          }
          client[command].apply(client, args.concat(callback));
        });

      });
  }

  // Called to establish a new connection, or use existing connections.
  // Resolves to a FiveBeans client.
  connect() {
    // This may be called multiple times, we only want to establish the
    // connection once, so we reuse the same promise
    if (this._connectPromise)
      return this._connectPromise;

    let connectPromise; // eslint-disable-line prefer-const

    const onClose = () => {
      // If client connection closed/end, discard the promise
      if (this._connectPromise === connectPromise)
        this._connectPromise = null;
    };

    connectPromise = this._connect(onClose)
      .catch(error => {
        onClose();
        throw new QueueError(error);
      });
    this._connectPromise = connectPromise;
    return connectPromise;
  }

  _connect(onClosed) {
    return Promise.resolve()
      .then(() => {

        // This is the Fivebeans client is essentially a session.
        const config  = this.config;
        const client  = new Fivebeans.client(config.host, config.port); // eslint-disable-line new-cap
        // For when we have a lot of writers contending to push to the same queue.
        client.setMaxListeners(0);

        // Once we established a connection, need to monitor for errors of when
        // server decides to close it
        client.once('error', error => {
          onClosed();
          debug('Client error in queue %s: %s', this.id, error.toString());
        });

        // This promise resolves when client connects.
        const establishConnection = new Promise(function(resolve, reject) {
          // Nothing happens until we start the connection.  Must wait for
          // connection event before we can send anything down the stream.
          client.connect();

          client.on('connect', () => resolve());
          client.on('error', reject);
          client.on('close', function() {
            reject(new QueueError('CLOSED'));
          });

          // Make sure we timeout on slow connections
          setTimeout(function() {
            reject(new Error('Timeout waiting for connection'));
          }, CONNECT_TIMEOUT);
        });

        return establishConnection
          .then(() => {

            // Watch for end and switch to new session
            client.stream.once('end', () => {
              onClosed();
              debug('Connection closed for %s', this.id);
            });

            // Put/reserve clients have different setup requirements, this are handled by
            // an externally supplied method.
            return this.setup(client);
          })
          .then(() => client);

      });
  }

  del(messageID, options, callback) {
    return this.request('destroy', messageID)
      .then(() => callback(), callback);
  }

  msg_release(messageID, reservationID, options, callback) { // eslint-disable-line camelcase
    const priority = 0;
    return this.request('release', messageID, priority, options.delay)
      .then(() => callback(), callback);
  }

  peek(options, callback) {
    const peekReady   = this.request('peek_ready').then(result => result[0]).catch(() => null);
    const peekDelayed = this.request('peek_delayed').then(result => result[0]).catch(() => null);
    return Promise.all([ peekReady, peekDelayed ])
      .then(function(ids) {
        const notNull = ids.filter(id => id);
        callback(null, notNull);
      })
      .catch(callback);
  }

  post(messageOrMessages, callback) {
    const messages = Array.isArray(messageOrMessages) ? messageOrMessages : [messageOrMessages];
    const priority = 0;
    const timeout  = ms('10m');
    const promises = messages.map(message => {
      return this.request('put', priority, message.delay, timeout, message.body);
    });
    return Promise.all(promises)
      .then(ids => callback(null, ids), callback);
  }

  reserve(options, callback) {
    return this.request('reserve_with_timeout', options.wait || 0)
      .then(function(job) {
        const id              = job[0];
        const body            = job[1];
        const reservationID   = id;
        const reservedCount   = 1;

        const message           = { id, body, reservedCount, reservationID };
        callback(null, message);
      })
      .catch(function(error) {
        if (error.message === 'TIMED_OUT')
          callback(null, null);
        else
          callback(error);
      });
  }

  clear(callback) {
    return peekAndDelete(this)
      .then(() => callback(), callback);

    function peekAndDelete(client) {
      return new Promise((resolve, reject) => {
        client.peek({}, (error, ids) => {
          if (error && /not.found/i.test(error))
            resolve();
          else if (error)
            reject(error);
          else if (ids.length) {
            const deleteEach  = ids.map(id => client.request('destroy', id));
            const deleteAll   = Promise.all(deleteEach);
            const andRepeat   = deleteAll.then(() => peekAndDelete(client));
            resolve(andRepeat);
          } else
            resolve();
        });
      });
    }
  }

};

<|endoftext|>


<commit_before>// These silly websites are hotlinking to rawgithub.com in production, which is
// totally against the rules. So we'll have some fun with them.
//
// This is an array of regexes that match referrer URLs.
module.exports = [
    /foxrio2\.com/i,
    /mix1079\.net/i,
    /recipebox\.tv/i,
    /stockdog\.tw/i
];
<commit_msg>Looks like evil.js got the message across.<commit_after>// These silly websites are hotlinking to rawgithub.com in production, which is
// totally against the rules. So we'll have some fun with them.
//
// This is an array of regexes that match referrer URLs.
module.exports = [
    /stockdog\.tw/i
];
<|endoftext|>


<commit_before>'use strict';
/* jshint node:true */
/* global module, process, console, require */
var help = require('./helpers'),
    out  = require('./helpers').out,
    conf = require('./config');

var bootstrapped = {};

/**
 * Function that is executed once, used to setup
 * logsene specific stuff
 * @private
 */
var bootstrap = function _bootstrap() {

  // bootstrap tracing
  // first check cli params (has priority)
  // for one-time trace, it must be together with search (not get or set)
  if (help.argv._.hasOwnProperty('search') && help.argv.hasOwnProperty('trace')) {
    //out.trace('argv trace: ' + help.argv.trace);
    help.enableTrace(help.argv.trace);

  } else if (typeof conf.getSync('trace') !== 'undefined') {  // from the current session
    //console.log('conf trace: ' + conf.getSync('trace'));
    help.enableTrace(conf.getSync('trace'));
  }

  // put API URIs from the configuration to the env (to avoid hard-coding them in the API)
  process.env.LOGSENE_ES_HOST = conf.logseneEsHost;
  process.env.LOGSENE_URI     = conf.logseneUri;
  //console.log('process.env.logsene-es-host: ' + process.env.LOGSENE_ES_HOST);
  //console.log('process.env.logsene-uri: ' + process.env.LOGSENE_URI);

  // make this a one time gig
  bootstrapped.done = true;
};

if (!bootstrapped.done) bootstrap();
<commit_msg>Avoid hardcoding Logsene URIs in the API<commit_after>'use strict';
/* jshint node:true */
/* global module, process, console, require */
var help = require('./helpers'),
    conf = require('./config');

var bootstrapped = {};

/**
 * Function that is executed once, used to setup
 * logsene specific stuff
 * @private
 */
var bootstrap = function _bootstrap() {

  // bootstrap tracing
  // first check cli params (has priority)
  // for one-time trace, it must be together with search (not get or set)
  if (help.argv._.hasOwnProperty('search') && help.argv.hasOwnProperty('trace')) {
    //console.log('argv trace: ' + help.argv.trace);
    help.enableTrace(help.argv.trace);

  } else if (typeof conf.getSync('trace') !== 'undefined') {  // from the current session
    //console.log('conf trace: ' + conf.getSync('trace'));
    help.enableTrace(conf.getSync('trace'));
  }

  // put API URIs from the configuration to the env (to avoid hard-coding them in the API)
  process.env.LOGSENE_ES_HOST = conf.logseneEsHost;
  process.env.LOGSENE_URI     = conf.logseneUri;
  //console.log('process.env.logsene-es-host: ' + process.env.LOGSENE_ES_HOST);
  //console.log('process.env.logsene-uri: ' + process.env.LOGSENE_URI);

  // make this a one time gig
  bootstrapped.done = true;
};

if (!bootstrapped.done) bootstrap();
<|endoftext|>