<commit_before>var methods = {
  save: function(fieldsNames, callback) {
    var doc = this;
    var Class = doc.constructor;

    // Handle arguments' types detection.
    if (arguments.length === 1) {
      if (_.isFunction(fieldsNames)) {
        callback = fieldsNames;
      }
    }
    if (_.isString(fieldsNames)) {
      fieldsNames = [fieldsNames];
    } else if (!_.isArray(fieldsNames)) {
      fieldsNames = Class.getFieldsNames();
    }

    // Trigger "beforeSave" event handlers on the current and parent classes.
    var event = new Astro.Event('beforeSave');
    event.target = doc;
    Class.emitEvent(event);
    // If user prevented default operation, then we have to stop here.
    if (event.defaultPrevented) {
      return;
    }

    // Set the flag indicating whether we are updating a document.
    var isUpdating = !doc._isNew;

    // Trigger "beforeInsert" or "beforeUpdate" event handlers on the current
    // and parent classes.
    event = new Astro.Event(isUpdating ? 'beforeUpdate' : 'beforeInsert');
    event.target = doc;
    Class.emitEvent(event);
    // If user prevented default operation, then we have to stop here.
    if (event.defaultPrevented) {
      return;
    }

    // Get values to update or insert.
    var values;
    if (doc._isNew) {
      values = Astro.utils.fields.getAllValues(doc, {
        cast: false
      });
    } else {
      values = Astro.utils.fields.getModifiedValues(doc, false, {
        cast: false
      });
    }

    // Get a value of the "_id" field.
    var id = Astro.utils.fields.getValue(doc, '_id');

    // Remove the "_id" field from the fields names list. We can only allow
    // saving the "_id" field, if it's a new doc with provided id. It means that
    // the developer wants that.
    if (!(values._id && doc._isNew)) {
      fieldsNames = _.without(fieldsNames, '_id');
    }

    // Pick only the values that the developer wants to save.
    values = _.pick(values, fieldsNames);

    // Modify fields list to only those that are present in the values.
    fieldsNames = _.keys(values);

    // Check if there are any values to update or insert. If there are no
    // modified fields, we shouldn't do anything.
    if (_.size(values) === 0) {
      return;
    }

    // Add callback to arguments list if provided.
    var args = [];
    if (_.isFunction(callback)) {
      args.push(callback);
    }

    // Get collection for given class or parent class.
    var Collection = Class.getCollection();

    var result;
    if (isUpdating) {
      // Add selector and modifier at the beginning of the arguments list. Right
      // now in the array is a callback function (if provided).
      args.unshift({ // Selector.
        _id: id
      }, { // Modifier.
        $set: values
      });
      // Update document.
      result = Collection.update.apply(Collection, args);
    } else {
      // Add values to insert into the list of arguments passed to the "insert"
      // method.
      args.unshift(values);
      // Insert document.
      result = Collection.insert.apply(Collection, args);
      Astro.utils.fields.setValue(doc, '_id', result);
    }

    // Change the "_isNew" flag to "false". Now the document is not new, it has
    // just been saved.
    doc._isNew = false;

    // Trigger "afterInsert" or "afterUpdate" event handlers on the current and
    // parent classes.
    var event = new Astro.Event(isUpdating ? 'afterUpdate' : 'afterInsert');
    event.target = doc;
    Class.emitEvent(event);
    // Trigger "afterSave" event handlers on the current and parent classes.
    var event = new Astro.Event('afterSave');
    event.target = doc;
    Class.emitEvent(event);

    // Copy all values to the "_original" property so that we are starting with
    // the clean object without modifications (there is no diff between current
    // values and "_original").
    doc._original = _.extend(doc._original, EJSON.clone(
      Astro.utils.fields.getValues(doc, fieldsNames)
    ));

    // Return result of executing Mongo query.
    return result;
  },

  remove: function(callback) {
    var doc = this;
    var Class = doc.constructor;

    // Remove only when document has the "_id" field (it's persisted).
    var id = Astro.utils.fields.getValue(doc, '_id');
    if (!id) {
      return;
    }

    // Trigger "beforeRemove" event handlers on the current and parent classes.
    var event = new Astro.Event('beforeRemove');
    event.target = doc;
    Class.emitEvent(event);

    // If user prevented default operation, then we have to stop here.
    if (event.defaultPrevented) {
      return;
    }

    // Add selector to arguments list.
    var args = [];
    args.push({
      _id: id
    });
    // Add callback to arguments list if provided.
    if (callback) {
      args.push(callback);
    }

    // Get collection for given class or parent class.
    var Collection = Class.getCollection();

    // Remove document and save result.
    var result = Collection.remove.apply(Collection, args);

    // Trigger "afterRemove" event handlers on the current and parent classes.
    var event = new Astro.Event('afterRemove');
    event.target = doc;
    Class.emitEvent(event);

    // Clear "_id" attribute and "_original" object, so that user can save
    // document one more time.
    Astro.utils.fields.setValue(doc, '_id', null);
    doc._original = {};

    // Return result of removing document.
    return result;
  },

  reload: function() {
    var doc = this;
    var Class = doc.constructor;

    // Get collection for given class or parent class.
    var Collection = Class.getCollection();

    // The document has to be already saved in the collection.
    var id = Astro.utils.fields.getValue(doc, '_id');
    if (id) {
      // Get new values from collection without the transformation.
      var attrs = Collection.findOne(id, {
        transform: null
      });

      // Init instance with the new values from the collection.
      Astro.BaseClass.call(doc, attrs);

      // Set the "_isNew" flag back to false.
      doc._isNew = false;
    }
  },

  copy: function(save) {
    var doc = this;
    save = save || false;

    // Use EJSON to clone object.
    var copy = EJSON.clone(doc);

    // Remove the "_id" value and set the "_isNew" flag to false so that it will
    // save the object as a new document instead updating the old one.
    Astro.utils.fields.setValue(copy, '_id', null);
    copy._original._id = null;
    copy._isNew = true;

    if (save) {
      copy.save();
    }

    return copy;
  }
};

var afterInit = function(attrs) {
  var doc = this;
  var Class = doc.constructor;

  doc.set(Class.getTypeField(), Class.getName());
};

Astro.eventManager.on('initClass', function(schemaDefinition) {
  var Class = this;

  // Create an object storing information about classes names connected to the
  // given collection and the "type" field.
  var Collection = Class.getCollection();
  if (!Collection) {
    return;
  }

  // Add storage methods, if a collection has been provided.
  _.extend(Class.prototype, methods);

  // Add the "_id" field.
  if (!Class.hasField('_id')) {
    Class.addField('_id', {
      type: 'string'
    });
  }

  // Add the "type" field, to distinguish to what class we have to cast a
  // document fetched from the collection.
  var typeField = Class.getTypeField();
  if (!typeField) {
    return;
  }

  if (!Class.hasField(typeField)) {
    Class.addField(typeField, {
      type: 'string'
    });
  }

  if (!Class.hasEvent('afterInit', afterInit)) {
    Class.addEvent('afterInit', afterInit);
  }
});
<commit_msg>Base for direct collection access<commit_after>var prototypeMethods = {};

/**
 * @summary Inserts or updates a document into the collection. Returns document _id on insert or modified documents count on update.
 * @locus Anywhere
 * @method save
 * @memberOf Astro.BaseClass
 * @instance
 * @param {Array} [fieldsNames] The list of fields that should only be saved into the collection.
 * @param {Function} [callback] Optional. If present, called with an error object as the first argument and, if no error, the _id or modified documents count as the second.
 */
prototypeMethods.save = function(fieldsNames, callback) {
  var doc = this;
  var Class = doc.constructor;

  // Handle arguments' types detection.
  if (arguments.length === 1) {
    if (_.isFunction(fieldsNames)) {
      callback = fieldsNames;
    }
  }
  if (_.isString(fieldsNames)) {
    fieldsNames = [fieldsNames];
  } else if (!_.isArray(fieldsNames)) {
    fieldsNames = Class.getFieldsNames();
  }

  // Trigger "beforeSave" event handlers on the current and parent classes.
  var event = new Astro.Event('beforeSave');
  event.target = doc;
  Class.emitEvent(event);
  // If user prevented default operation, then we have to stop here.
  if (event.defaultPrevented) {
    return;
  }

  // Set the flag indicating whether we are updating a document.
  var isUpdating = !doc._isNew;

  // Trigger "beforeInsert" or "beforeUpdate" event handlers on the current
  // and parent classes.
  event = new Astro.Event(isUpdating ? 'beforeUpdate' : 'beforeInsert');
  event.target = doc;
  Class.emitEvent(event);
  // If user prevented default operation, then we have to stop here.
  if (event.defaultPrevented) {
    return;
  }

  // Get values to update or insert.
  var values;
  if (doc._isNew) {
    values = Astro.utils.fields.getAllValues(doc, {
      cast: false
    });
  } else {
    values = Astro.utils.fields.getModifiedValues(doc, false, {
      cast: false
    });
  }

  // Get a value of the "_id" field.
  var id = Astro.utils.fields.getValue(doc, '_id');

  // Remove the "_id" field from the fields names list. We can only allow
  // saving the "_id" field, if it's a new doc with provided id. It means that
  // the developer wants that.
  if (!(values._id && doc._isNew)) {
    fieldsNames = _.without(fieldsNames, '_id');
  }

  // Pick only the values that the developer wants to save.
  values = _.pick(values, fieldsNames);

  // Modify fields list to only those that are present in the values.
  fieldsNames = _.keys(values);

  // Check if there are any values to update or insert. If there are no
  // modified fields, we shouldn't do anything.
  if (_.size(values) === 0) {
    return;
  }

  // Add callback to arguments list if provided.
  var args = [];
  if (_.isFunction(callback)) {
    args.push(callback);
  }

  // Get collection for given class or parent class.
  var Collection = Class.getCollection();

  var result;
  if (isUpdating) {
    // Add selector and modifier at the beginning of the arguments list. Right
    // now in the array is a callback function (if provided).
    args.unshift({ // Selector.
      _id: id
    }, { // Modifier.
      $set: values
    });
    // Update document.
    result = Collection.update.apply(Collection, args);
  } else {
    // Add values to insert into the list of arguments passed to the "insert"
    // method.
    args.unshift(values);
    // Insert document.
    result = Collection.insert.apply(Collection, args);
    Astro.utils.fields.setValue(doc, '_id', result);
  }

  // Change the "_isNew" flag to "false". Now the document is not new, it has
  // just been saved.
  doc._isNew = false;

  // Trigger "afterInsert" or "afterUpdate" event handlers on the current and
  // parent classes.
  var event = new Astro.Event(isUpdating ? 'afterUpdate' : 'afterInsert');
  event.target = doc;
  Class.emitEvent(event);
  // Trigger "afterSave" event handlers on the current and parent classes.
  var event = new Astro.Event('afterSave');
  event.target = doc;
  Class.emitEvent(event);

  // Copy all values to the "_original" property so that we are starting with
  // the clean object without modifications (there is no diff between current
  // values and "_original").
  doc._original = _.extend(doc._original, EJSON.clone(
    Astro.utils.fields.getValues(doc, fieldsNames)
  ));

  // Return result of executing Mongo query.
  return result;
};

/**
 * @summary Removes a document from the collection. Returns an amount of removed documents.
 * @locus Anywhere
 * @method remove
 * @memberOf Astro.BaseClass
 * @instance
 * @param {Function} [callback] Optional. If present, called with an error object as the first argument and, if no error, the an amount of removed documents as the second.
 */
prototypeMethods.remove = function(callback) {
  var doc = this;
  var Class = doc.constructor;

  // Remove only when document has the "_id" field (it's persisted).
  var id = Astro.utils.fields.getValue(doc, '_id');
  if (!id) {
    return;
  }

  // Trigger "beforeRemove" event handlers on the current and parent classes.
  var event = new Astro.Event('beforeRemove');
  event.target = doc;
  Class.emitEvent(event);

  // If user prevented default operation, then we have to stop here.
  if (event.defaultPrevented) {
    return;
  }

  // Add selector to arguments list.
  var args = [];
  args.push({
    _id: id
  });
  // Add callback to arguments list if provided.
  if (callback) {
    args.push(callback);
  }

  // Get collection for given class or parent class.
  var Collection = Class.getCollection();

  // Remove document and save result.
  var result = Collection.remove.apply(Collection, args);

  // Trigger "afterRemove" event handlers on the current and parent classes.
  var event = new Astro.Event('afterRemove');
  event.target = doc;
  Class.emitEvent(event);

  // Clear "_id" attribute and "_original" object, so that user can save
  // document one more time.
  Astro.utils.fields.setValue(doc, '_id', null);
  doc._original = {};

  // Return result of removing document.
  return result;
};

prototypeMethods.reload = function() {
  var doc = this;
  var Class = doc.constructor;

  // Get collection for given class or parent class.
  var Collection = Class.getCollection();

  // The document has to be already saved in the collection.
  var id = Astro.utils.fields.getValue(doc, '_id');
  if (id) {
    // Get new values from collection without the transformation.
    var attrs = Collection.findOne(id, {
      transform: null
    });

    // Init instance with the new values from the collection.
    Astro.BaseClass.call(doc, attrs);

    // Set the "_isNew" flag back to false.
    doc._isNew = false;
  }
};

prototypeMethods.copy = function(save) {
  var doc = this;
  save = save || false;

  // Use EJSON to clone object.
  var copy = EJSON.clone(doc);

  // Remove the "_id" value and set the "_isNew" flag to false so that it will
  // save the object as a new document instead updating the old one.
  Astro.utils.fields.setValue(copy, '_id', null);
  copy._original._id = null;
  copy._isNew = true;

  if (save) {
    copy.save();
  }

  return copy;
};

var classMethods = {};

/**
 * @summary Inserts a document into the collection.
 * @locus Anywhere
 * @method insert
 * @memberOf Astro.BaseClass
 * @class
 * @param {Object} [doc] A doc to insert.
 * @param {Function} [callback] Optional. If present, called with an error object as the first argument and, if no error, the _id as the second.
 * @returns {String} Returns an _id of an inserted document.
 */
classMethods.insert = function(doc, callback) {
  var Class = this;

  var doc = new Class(doc);
  doc.save();
};

/**
 * @summary Updates documents in the collection.
 * @return
 * @locus update
 * @method insert
 * @memberOf Astro.BaseClass
 * @class
 * @param {MongoSelector} selector Specifies which documents to modify
 * @param {MongoModifier} modifier Specifies how to modify the documents
 * @param {Object} [options]
 * @param {Boolean} options.multi True to modify all matching documents; false to only modify one of the matching documents (the default).
 * @param {Boolean} options.upsert True to insert a document if no matching documents are found.
 * @param {Function} [callback] Optional. If present, called with an error object as the first argument and, if no error, the number of affected documents as the second.
 * @returns {Number} Returns number of documents that have been modified.
 */
classMethods.update = function(selector, modifier, options, callback) {
  var Class = this;
  var Collection = Class.getCollection();

  // The options argument is optional and if there are only three arguments,
  // then it may mean that the last argument is a callback function not options.
  if (arguments.length === 3 && _.isFunction(options)) {
    callback = options;
    options = {};
  }

  // Make sure that options object is created.
  options = options || {};

  // We select one or many documents depending on the "multi" flag.
  var docs;
  if (options.multi) {
    docs = Collection.find(selector);
  } else {
    docs = Collection.find(selector, {
      limit: 1
    });
  }

  docs.forEach(function(doc) {
    if (modifier.$set) {
      doc.set(modifier.$set);
    }

    if (modifier.$push) {
      doc.push(modifier.$push);
    }

    console.log(doc.get());
  });
};

classMethods.remove = function() {
};

var afterInit = function(attrs) {
  var doc = this;
  var Class = doc.constructor;

  doc.set(Class.getTypeField(), Class.getName());
};

Astro.eventManager.on('initClass', function(schemaDefinition) {
  var Class = this;

  // Create an object storing information about classes names connected to the
  // given collection and the "type" field.
  var Collection = Class.getCollection();
  if (!Collection) {
    return;
  }

  // Add storage methods to the class prototype.
  _.extend(Class.prototype, prototypeMethods);

  // Add storage methods to the class.
  _.extend(Class, classMethods);

  // Add the "_id" field.
  if (!Class.hasField('_id')) {
    Class.addField('_id', {
      type: 'string'
    });
  }

  // Add the "type" field, to distinguish to what class we have to cast a
  // document fetched from the collection.
  var typeField = Class.getTypeField();
  if (!typeField) {
    return;
  }

  if (!Class.hasField(typeField)) {
    Class.addField(typeField, {
      type: 'string'
    });
  }

  if (!Class.hasEvent('afterInit', afterInit)) {
    Class.addEvent('afterInit', afterInit);
  }
});
<|endoftext|>


<commit_before>var Promise = require('rsvp').Promise;
var asp = require('rsvp').denodeify;
var fs = require('fs');
var path = require('path');
var url = require('url');
var createHash = require('crypto').createHash;
var template = require('es6-template-strings');
var getAlias = require('./utils').getAlias;
var getTreeModulesPostOrder = require('./trace').getTreeModulesPostOrder;
var extend = require('./utils').extend;
var dextend = require('./utils').dextend;
var getPackage = require('./utils').getPackage;

var compilerMap = {
  'amd': '../compilers/amd',
  'cjs': '../compilers/cjs',
  'esm': '../compilers/esm',
  'global': '../compilers/global',
  'register': '../compilers/register'
};

// create a compile hash based on path + source + metadata + compileOpts
// one implication here is that plugins shouldn't rely on System.x checks
// as these will not be cache-invalidated but within the bundle hook is fine
function getCompileHash(load, compileOpts) {
  return createHash('md5')
  .update(JSON.stringify({
    source: load.source,
    metadata: load.metadata,
    path: compileOpts.sourceMaps && load.path,

    normalize: compileOpts.normalize,
    anonymous: compileOpts.anonymous,
    systemGlobal: compileOpts.systemGlobal,
    static: compileOpts.static,
    encodeNames: compileOpts.encodeNames,
    sourceMaps: compileOpts.sourceMaps,
    lowResSourceMaps: compileOpts.lowResSourceMaps
  }))
  .digest('hex');
}

function getEncoding(canonical, encodings) {
  // dont encode system modules
  if (canonical[0] == '@')
    return canonical;

  // return existing encoding if present
  if (encodings[canonical])
    return encodings[canonical];

  // search for the first available key
  var highestEncoding = 0;
  Object.keys(encodings).forEach(function(canonical) {
    var encoding = encodings[canonical];
    highestEncoding = Math.max(parseInt(encoding, '16'), highestEncoding);
  });

  highestEncoding++;

  return encodings[canonical] = highestEncoding.toString(16);
}

exports.compileLoad = compileLoad;
function compileLoad(loader, load, compileOpts, cache) {
  var format = load.metadata.format;

  if (format == 'defined')
    return Promise.resolve({ source:  compileOpts.systemGlobal + '.register("' + load.name + '", [], function() { return { setters: [], execute: function() {} } });\n' });

  if (format in compilerMap) {
    // use cached if we have it
    var cached = cache.loads[load.name];
    if (cached && cached.hash == getCompileHash(load, compileOpts))
      return Promise.resolve(cached.output);

    // create a new load record with any necessary final mappings
    function remapLoadRecord(load, mapFunction) {
      load = extend({}, load);
      load.name = mapFunction(load.name, load.name);
      var depMap = {};
      Object.keys(load.depMap).forEach(function(dep) {
        depMap[dep] = mapFunction(load.depMap[dep], dep);
      });
      load.depMap = depMap;
      return load;
    }

    var mappedload = remapLoadRecord(load, function(name, original) {
      // do SFX encodings
      if (compileOpts.encodeNames)
        return getEncoding(name, cache.encodings);

      // strip package conditional syntax at compile time
      return name.replace('#:', '/');
    });

    return Promise.resolve(require(compilerMap[format]).compile(mappedload, compileOpts, loader))
    .then(function(output) {
      // store compiled output in cache
      cache.loads[load.name] = {
        hash: getCompileHash(load, compileOpts),
        output: output
      };

      return output;
    })
  }

  return Promise.reject(new TypeError('Unknown module format ' + format));
}

exports.compileTree = compileTree;
function compileTree(loader, tree, compileOpts, outputOpts, cache) {
  // sort in graph order, filter modules to actually built loads (excluding conditionals, build: false)
  var ordered = getTreeModulesPostOrder(tree);
  // get entrypoints from graph algorithm
  var entryPoints = ordered.entryPoints;
  var modules = ordered.modules.filter(function(moduleName) {
    return tree[moduleName] && !tree[moduleName].conditional;
  });

  if (compileOpts.encodeNames)
    entryPoints = entryPoints.map(function(name) {
      return getEncoding(name, cache.encodings)
    });

  var outputs = [];

  // store plugins with a bundle hook to allow post-processing
  var pluginLoads = {};
  var compilers = {};

  // create load output objects
  return Promise.all(modules.map(function(name) {
    return Promise.resolve()
    .then(function() {
      var load = tree[name];

      if (load === true)
        throw new TypeError(name + ' was defined via a bundle, so can only be used for subtraction or union operations.');

      if (compileOpts.static && load.runtimePlugin)
        throw new TypeError('Plugin ' + load.runtimePlugin + ' does not support static builds, compiling ' + load.name + '.');

      // store plugin loads for bundle hook
      if (load.metadata.loader && load.metadata.loaderModule.bundle) {
        var pluginLoad = extend({}, load);
        pluginLoad.address = System.baseURL + load.path;
        (pluginLoads[load.metadata.loader] = pluginLoads[load.metadata.loader] || []).push(pluginLoad);
      }

      return compileLoad(loader, tree[name], compileOpts, cache);
    })
    .then(function(output) {
      outputs.push(output);
    });
  }))

  // run plugin bundle hook
  .then(function() {
    return Promise.all(Object.keys(pluginLoads).map(function(pluginName) {
      var loads = pluginLoads[pluginName];
      var bundle = loads[0].metadata.loaderModule.bundle;

      // NB deprecate the 2 argument form
      return Promise.resolve(bundle.length < 3
          ? bundle.call(loader.pluginLoader, loads, extend(extend({}, compileOpts), outputOpts))
          : bundle.call(loader.pluginLoader, loads, compileOpts, outputOpts))
      .then(function(output) {
        if (output instanceof Array)
          outputs = outputs.concat(output);
        else
          outputs.push(output);
      });
    }));
  })
  .then(function() {

    // if any module in the bundle is AMD, add a "bundle" meta to the bundle
    // this can be deprecated if https://github.com/systemjs/builder/issues/264 lands
    if (modules.some(function(name) {
          return tree[name].metadata.format == 'amd';
        }))
      outputs.unshift('"bundle";');

    // static bundle wraps with a self-executing loader
    if (compileOpts.static)
      return wrapSFXOutputs(loader, tree, outputs, entryPoints, compileOpts);
    else
      return addPackageConfig(loader, tree, outputs, entryPoints, compileOpts);
  })
  .then(function(outputs) {
    // NB also include all aliases of all entryPoints along with entryPoints
    return {
      outputs: outputs,
      entryPoints: entryPoints
    };
  });
}

function addPackageConfig(loader, tree, outputs, entryPoints, compileOpts) {
  if (!compileOpts.buildConfig)
    return outputs;

  var bundleConfig;

  // given a package and subpath, return the config used by the subpath resolution
  var normalizedPkgMains = {};
  function getConfigFor(pkgName, subPath) {
    var pkg = loader.packages[pkgName];

    var cfg;

    // if this subPath is the normalized package main, include the main in the config
    if (pkg.main) {
      var main = normalizedPkgMains[pkgName] = normalizedPkgMains[pkgName] || loader.normalizeSync(pkgName).substr(pkgName.length + 1);
      if (main == subPath)
        (cfg = cfg || {}).main = pkg.main;
    }

    // meta for 'file.js' informs us that if the default extension is 'ts', then import "file.js" must not import "file.js.ts"
    // this is important to include here
    // NB meta building here
    // wildcard meta
    /* var wildcardIndex;
    for (var module in pkg.meta) {
      // allow meta to start with ./ for flexibility
      var dotRel = module.substr(0, 2) == './' ? './' : '';
      if (dotRel)
        module = module.substr(2);

      wildcardIndex = module.indexOf('*');
      if (wildcardIndex === -1)
        continue;

      if (module.substr(0, wildcardIndex) == subPath.substr(0, wildcardIndex)
          && module.substr(wildcardIndex + 1) == subPath.substr(subPath.length - module.length + wildcardIndex + 1)) {
        cfg = cfg || {};
        cfg.meta = cfg.meta || {};
        for (var m in pkg.meta[module])
          cfg.meta[m] = true;
      }
    }
    // exact meta
    var exactMeta = pkgMeta[subPath] || pkgMeta['./' + subPath];
    if (exactMeta)
      matchFn(exactMeta, exactMeta, 0); */

    // reverse-apply map configs to see which ones could result in this subPath
    // adding any that apply
    var target;
    for (var p in pkg.map) {
      if (typeof pkg.map[p] != 'object') {
        if (pkg.map[p].substr(0, 2) != './')
          continue;

        target = pkg.map[p].substr(2);
        if (subPath.substr(0, target.length) == target && (subPath.length == target.length || subPath[target.length] == '/'))
          ((cfg = cfg || {}).map = cfg.map || {})[p] = pkg.map[p];
        continue;
      }

      for (var q in pkg.map[p]) {
        if (pkg.map[p][q].substr(0, 2) != './')
          continue;

        target = pkg.map[p][q].substr(2);
        if (subPath.substr(0, target.length) == target && (subPath.length == target.length || subPath[target.length] == '/'))
          (((cfg = cfg || {}).map = cfg.map || {})[p] = cfg.map[p] || {})[q] = pkg.map[p][q];
      }
    }

    return cfg;
  }

  Object.keys(tree).forEach(function(m) {
    // ensure we include conditional package configs
    m = m.replace('#:', '/');
    // check if this module is in a package
    var normalized = loader.normalizeSync(m);
    var packageName = getPackage(loader.packages, normalized);
    if (packageName) {
      canonicalPackageName = m.substr(0, m.length - (normalized.length - packageName.length));
      bundleConfig = bundleConfig || { packages: {} };
      bundleConfig.packages[canonicalPackageName] = bundleConfig.packages[canonicalPackageName] || {};
      dextend(bundleConfig.packages[canonicalPackageName], getConfigFor(packageName, normalized.substr(packageName.length + 1)));
    }
  });

  if (bundleConfig)
    outputs.push(compileOpts.systemGlobal + '.config(' + JSON.stringify(bundleConfig, null, 2) + ');');

  return outputs;
}

exports.wrapSFXOutputs = wrapSFXOutputs;
function wrapSFXOutputs(loader, tree, outputs, entryPoints, compileOpts) {
  var compilers = {};

  // NB deprecate
  if (compileOpts.format == 'es6')
    compileOpts.format = 'esm';

  var modules = Object.keys(tree).filter(function(module) {
    return tree[module] && !tree[module].conditional;
  });

  // determine compilers used
  modules.forEach(function(name) {
    compilers[tree[name].metadata.format] = true;
  });

  // include compiler helpers at the beginning of outputs
  Object.keys(compilerMap).forEach(function(format) {
    if (!compilers[format])
      return;
    var compiler = require(compilerMap[format]);
    if (compiler.sfx)
      outputs.unshift(compiler.sfx(loader));
  });

  // determine if the SFX bundle has any external dependencies it relies on
  var externalDeps = [];
  var externalDepIds = [];
  var globalDeps = [];
  modules.forEach(function(name) {
    var load = tree[name];

    // check all deps are present
    load.deps.forEach(function(dep) {
      var key = load.depMap[dep];
      if (!(key in tree) && key[0] != '@') {
        if (compileOpts.format == 'esm')
          throw new TypeError('External SFX dependencies not yet supported for ES module SFX bundles. See https://github.com/systemjs/builder/issues/259.')

        var alias = getAlias(loader, key);

        if (compileOpts.format == 'global') {
          if (!compileOpts.globalDeps[alias])
            throw new TypeError('Global SFX bundle dependency "' + alias + '" (' + key + ') must be configured to an environment global via the globalDeps option.');
          globalDeps.push(compileOpts.globalDeps[alias]);
        }

        externalDeps.push(alias);
        externalDepIds.push(key);
      }
    });
  });

  // next wrap with the core code
  return asp(fs.readFile)(path.resolve(__dirname, '../templates/sfx-core.js'))
  .then(function(sfxcore) {
    outputs.unshift(sfxcore.toString(), "(['" + entryPoints.join('\', \'') + "'], " + JSON.stringify(externalDepIds) + ", function(" + compileOpts.systemGlobal + ") {\n");

    outputs.push("})");
    return asp(fs.readFile)(path.resolve(__dirname, '../templates/sfx-' + compileOpts.format + '.js'))
  })
  // then include the sfx module format wrapper
  .then(function(formatWrapper) {
    outputs.push(template(formatWrapper.toString(), {
      deps: externalDeps,
      globalDeps: globalDeps,
      globalName: compileOpts.globalName
    }));
  })
  // then wrap with the runtime
  .then(function() {
    var usesBabelHelpersGlobal = modules.some(function(name) {
      return tree[name].metadata.usesBabelHelpersGlobal;
    });
    if (compileOpts.runtime && usesBabelHelpersGlobal)
      return getModuleSource(loader, 'babel/external-helpers')
      .then(function(source) {
        outputs.unshift(source);
      });
  })
  .then(function() {
    var usesTraceurRuntimeGlobal = modules.some(function(name) {
      return tree[name].metadata.usesTraceurRuntimeGlobal;
    });
    if (compileOpts.runtime && usesTraceurRuntimeGlobal)
      return getModuleSource(loader, 'traceur-runtime')
      .then(function(source) {
        // protect System global clobbering
        outputs.unshift("(function(){ var curSystem = typeof System != 'undefined' ? System : undefined;\n" + source + "\nSystem = curSystem; })();");
      });
  })
  // for AMD, CommonJS and global SFX outputs, add a "format " meta to support SystemJS loading
  .then(function() {
    if (compileOpts.globalName)
      outputs.unshift('"exports ' + compileOpts.globalName + '";');

    if (compileOpts.format == 'global') {
      for (var g in compileOpts.globalDeps)
        outputs.unshift('"globals.' + compileOpts.globalDeps[g] + ' ' + g + '";');
    }

    if (compileOpts.format == 'amd' || compileOpts.format == 'cjs' || compileOpts.format == 'global')
      outputs.unshift('"format ' + compileOpts.format + '";');
  })
  .then(function() {
    return outputs;
  });
}

exports.attachCompilers = function(loader) {
  Object.keys(compilerMap).forEach(function(compiler) {
    var attach = require(compilerMap[compiler]).attach;
    if (attach)
      attach(loader);
  });
};

function getModuleSource(loader, module) {
  return loader.normalize(module)
  .then(function(normalized) {
    return loader.locate({ name: normalized, metadata: {} });
  })
  .then(function(address) {
    return loader.fetch({ address: address, metadata: {} });
  })
  .then(function(fetched) {
    // allow to be a redirection module
    var redirection = fetched.toString().match(/^\s*module\.exports = require\(\"([^\"]+)\"\);\s*$/);
    if (redirection)
      return getModuleSource(loader, redirection[1]);
    return fetched;
  })
  .catch(function(err) {
    console.log('Unable to find helper module "' + module + '". Make sure it is configured in the builder.');
    throw err;
  });
}
<commit_msg>sfx stubbing fix<commit_after>var Promise = require('rsvp').Promise;
var asp = require('rsvp').denodeify;
var fs = require('fs');
var path = require('path');
var url = require('url');
var createHash = require('crypto').createHash;
var template = require('es6-template-strings');
var getAlias = require('./utils').getAlias;
var getTreeModulesPostOrder = require('./trace').getTreeModulesPostOrder;
var extend = require('./utils').extend;
var dextend = require('./utils').dextend;
var getPackage = require('./utils').getPackage;

var compilerMap = {
  'amd': '../compilers/amd',
  'cjs': '../compilers/cjs',
  'esm': '../compilers/esm',
  'global': '../compilers/global',
  'register': '../compilers/register'
};

// create a compile hash based on path + source + metadata + compileOpts
// one implication here is that plugins shouldn't rely on System.x checks
// as these will not be cache-invalidated but within the bundle hook is fine
function getCompileHash(load, compileOpts) {
  return createHash('md5')
  .update(JSON.stringify({
    source: load.source,
    metadata: load.metadata,
    path: compileOpts.sourceMaps && load.path,

    normalize: compileOpts.normalize,
    anonymous: compileOpts.anonymous,
    systemGlobal: compileOpts.systemGlobal,
    static: compileOpts.static,
    encodeNames: compileOpts.encodeNames,
    sourceMaps: compileOpts.sourceMaps,
    lowResSourceMaps: compileOpts.lowResSourceMaps
  }))
  .digest('hex');
}

function getEncoding(canonical, encodings) {
  // dont encode system modules
  if (canonical[0] == '@')
    return canonical;

  // return existing encoding if present
  if (encodings[canonical])
    return encodings[canonical];

  // search for the first available key
  var highestEncoding = 0;
  Object.keys(encodings).forEach(function(canonical) {
    var encoding = encodings[canonical];
    highestEncoding = Math.max(parseInt(encoding, '16'), highestEncoding);
  });

  highestEncoding++;

  return encodings[canonical] = highestEncoding.toString(16);
}

exports.compileLoad = compileLoad;
function compileLoad(loader, load, compileOpts, cache) {

  // use cached if we have it
  var cached = cache.loads[load.name];
  if (cached && cached.hash == getCompileHash(load, compileOpts))
    return Promise.resolve(cached.output);

  // create a new load record with any necessary final mappings
  function remapLoadRecord(load, mapFunction) {
    load = extend({}, load);
    load.name = mapFunction(load.name, load.name);
    var depMap = {};
    Object.keys(load.depMap).forEach(function(dep) {
      depMap[dep] = mapFunction(load.depMap[dep], dep);
    });
    load.depMap = depMap;
    return load;
  }
  var mappedLoad = remapLoadRecord(load, function(name, original) {
    // do SFX encodings
    if (compileOpts.encodeNames)
      return getEncoding(name, cache.encodings);

    // strip package conditional syntax at compile time
    return name.replace('#:', '/');
  });

  var format = load.metadata.format;

  if (format == 'defined')
    return Promise.resolve({ source:  compileOpts.systemGlobal + '.register("' + mappedLoad.name + '", [], function() { return { setters: [], execute: function() {} } });\n' });

  if (format in compilerMap)
    return Promise.resolve(require(compilerMap[format]).compile(mappedLoad, compileOpts, loader))
    .then(function(output) {
      // store compiled output in cache
      cache.loads[load.name] = {
        hash: getCompileHash(load, compileOpts),
        output: output
      };

      return output;
    });

  return Promise.reject(new TypeError('Unknown module format ' + format));
}

exports.compileTree = compileTree;
function compileTree(loader, tree, compileOpts, outputOpts, cache) {
  // sort in graph order, filter modules to actually built loads (excluding conditionals, build: false)
  var ordered = getTreeModulesPostOrder(tree);
  // get entrypoints from graph algorithm
  var entryPoints = ordered.entryPoints;
  var modules = ordered.modules.filter(function(moduleName) {
    return tree[moduleName] && !tree[moduleName].conditional;
  });

  if (compileOpts.encodeNames)
    entryPoints = entryPoints.map(function(name) {
      return getEncoding(name, cache.encodings)
    });

  var outputs = [];

  // store plugins with a bundle hook to allow post-processing
  var pluginLoads = {};
  var compilers = {};

  // create load output objects
  return Promise.all(modules.map(function(name) {
    return Promise.resolve()
    .then(function() {
      var load = tree[name];

      if (load === true)
        throw new TypeError(name + ' was defined via a bundle, so can only be used for subtraction or union operations.');

      if (compileOpts.static && load.runtimePlugin)
        throw new TypeError('Plugin ' + load.runtimePlugin + ' does not support static builds, compiling ' + load.name + '.');

      // store plugin loads for bundle hook
      if (load.metadata.loader && load.metadata.loaderModule.bundle) {
        var pluginLoad = extend({}, load);
        pluginLoad.address = System.baseURL + load.path;
        (pluginLoads[load.metadata.loader] = pluginLoads[load.metadata.loader] || []).push(pluginLoad);
      }

      return compileLoad(loader, tree[name], compileOpts, cache);
    })
    .then(function(output) {
      outputs.push(output);
    });
  }))

  // run plugin bundle hook
  .then(function() {
    return Promise.all(Object.keys(pluginLoads).map(function(pluginName) {
      var loads = pluginLoads[pluginName];
      var bundle = loads[0].metadata.loaderModule.bundle;

      // NB deprecate the 2 argument form
      return Promise.resolve(bundle.length < 3
          ? bundle.call(loader.pluginLoader, loads, extend(extend({}, compileOpts), outputOpts))
          : bundle.call(loader.pluginLoader, loads, compileOpts, outputOpts))
      .then(function(output) {
        if (output instanceof Array)
          outputs = outputs.concat(output);
        else
          outputs.push(output);
      });
    }));
  })
  .then(function() {

    // if any module in the bundle is AMD, add a "bundle" meta to the bundle
    // this can be deprecated if https://github.com/systemjs/builder/issues/264 lands
    if (modules.some(function(name) {
          return tree[name].metadata.format == 'amd';
        }))
      outputs.unshift('"bundle";');

    // static bundle wraps with a self-executing loader
    if (compileOpts.static)
      return wrapSFXOutputs(loader, tree, outputs, entryPoints, compileOpts);
    else
      return addPackageConfig(loader, tree, outputs, entryPoints, compileOpts);
  })
  .then(function(outputs) {
    // NB also include all aliases of all entryPoints along with entryPoints
    return {
      outputs: outputs,
      entryPoints: entryPoints
    };
  });
}

function addPackageConfig(loader, tree, outputs, entryPoints, compileOpts) {
  if (!compileOpts.buildConfig)
    return outputs;

  var bundleConfig;

  // given a package and subpath, return the config used by the subpath resolution
  var normalizedPkgMains = {};
  function getConfigFor(pkgName, subPath) {
    var pkg = loader.packages[pkgName];

    var cfg;

    // if this subPath is the normalized package main, include the main in the config
    if (pkg.main) {
      var main = normalizedPkgMains[pkgName] = normalizedPkgMains[pkgName] || loader.normalizeSync(pkgName).substr(pkgName.length + 1);
      if (main == subPath)
        (cfg = cfg || {}).main = pkg.main;
    }

    // meta for 'file.js' informs us that if the default extension is 'ts', then import "file.js" must not import "file.js.ts"
    // this is important to include here
    // NB meta building here
    // wildcard meta
    /* var wildcardIndex;
    for (var module in pkg.meta) {
      // allow meta to start with ./ for flexibility
      var dotRel = module.substr(0, 2) == './' ? './' : '';
      if (dotRel)
        module = module.substr(2);

      wildcardIndex = module.indexOf('*');
      if (wildcardIndex === -1)
        continue;

      if (module.substr(0, wildcardIndex) == subPath.substr(0, wildcardIndex)
          && module.substr(wildcardIndex + 1) == subPath.substr(subPath.length - module.length + wildcardIndex + 1)) {
        cfg = cfg || {};
        cfg.meta = cfg.meta || {};
        for (var m in pkg.meta[module])
          cfg.meta[m] = true;
      }
    }
    // exact meta
    var exactMeta = pkgMeta[subPath] || pkgMeta['./' + subPath];
    if (exactMeta)
      matchFn(exactMeta, exactMeta, 0); */

    // reverse-apply map configs to see which ones could result in this subPath
    // adding any that apply
    var target;
    for (var p in pkg.map) {
      if (typeof pkg.map[p] != 'object') {
        if (pkg.map[p].substr(0, 2) != './')
          continue;

        target = pkg.map[p].substr(2);
        if (subPath.substr(0, target.length) == target && (subPath.length == target.length || subPath[target.length] == '/'))
          ((cfg = cfg || {}).map = cfg.map || {})[p] = pkg.map[p];
        continue;
      }

      for (var q in pkg.map[p]) {
        if (pkg.map[p][q].substr(0, 2) != './')
          continue;

        target = pkg.map[p][q].substr(2);
        if (subPath.substr(0, target.length) == target && (subPath.length == target.length || subPath[target.length] == '/'))
          (((cfg = cfg || {}).map = cfg.map || {})[p] = cfg.map[p] || {})[q] = pkg.map[p][q];
      }
    }

    return cfg;
  }

  Object.keys(tree).forEach(function(m) {
    // ensure we include conditional package configs
    m = m.replace('#:', '/');
    // check if this module is in a package
    var normalized = loader.normalizeSync(m);
    var packageName = getPackage(loader.packages, normalized);
    if (packageName) {
      canonicalPackageName = m.substr(0, m.length - (normalized.length - packageName.length));
      bundleConfig = bundleConfig || { packages: {} };
      bundleConfig.packages[canonicalPackageName] = bundleConfig.packages[canonicalPackageName] || {};
      dextend(bundleConfig.packages[canonicalPackageName], getConfigFor(packageName, normalized.substr(packageName.length + 1)));
    }
  });

  if (bundleConfig)
    outputs.push(compileOpts.systemGlobal + '.config(' + JSON.stringify(bundleConfig, null, 2) + ');');

  return outputs;
}

exports.wrapSFXOutputs = wrapSFXOutputs;
function wrapSFXOutputs(loader, tree, outputs, entryPoints, compileOpts) {
  var compilers = {};

  // NB deprecate
  if (compileOpts.format == 'es6')
    compileOpts.format = 'esm';

  var modules = Object.keys(tree).filter(function(module) {
    return tree[module] && !tree[module].conditional;
  });

  // determine compilers used
  modules.forEach(function(name) {
    compilers[tree[name].metadata.format] = true;
  });

  // include compiler helpers at the beginning of outputs
  Object.keys(compilerMap).forEach(function(format) {
    if (!compilers[format])
      return;
    var compiler = require(compilerMap[format]);
    if (compiler.sfx)
      outputs.unshift(compiler.sfx(loader));
  });

  // determine if the SFX bundle has any external dependencies it relies on
  var externalDeps = [];
  var externalDepIds = [];
  var globalDeps = [];
  modules.forEach(function(name) {
    var load = tree[name];

    // check all deps are present
    load.deps.forEach(function(dep) {
      var key = load.depMap[dep];
      if (!(key in tree) && key[0] != '@') {
        if (compileOpts.format == 'esm')
          throw new TypeError('External SFX dependencies not yet supported for ES module SFX bundles. See https://github.com/systemjs/builder/issues/259.')

        var alias = getAlias(loader, key);

        if (compileOpts.format == 'global') {
          if (!compileOpts.globalDeps[alias])
            throw new TypeError('Global SFX bundle dependency "' + alias + '" (' + key + ') must be configured to an environment global via the globalDeps option.');
          globalDeps.push(compileOpts.globalDeps[alias]);
        }

        externalDeps.push(alias);
        externalDepIds.push(key);
      }
    });
  });

  // next wrap with the core code
  return asp(fs.readFile)(path.resolve(__dirname, '../templates/sfx-core.js'))
  .then(function(sfxcore) {
    outputs.unshift(sfxcore.toString(), "(['" + entryPoints.join('\', \'') + "'], " + JSON.stringify(externalDepIds) + ", function(" + compileOpts.systemGlobal + ") {\n");

    outputs.push("})");
    return asp(fs.readFile)(path.resolve(__dirname, '../templates/sfx-' + compileOpts.format + '.js'))
  })
  // then include the sfx module format wrapper
  .then(function(formatWrapper) {
    outputs.push(template(formatWrapper.toString(), {
      deps: externalDeps,
      globalDeps: globalDeps,
      globalName: compileOpts.globalName
    }));
  })
  // then wrap with the runtime
  .then(function() {
    var usesBabelHelpersGlobal = modules.some(function(name) {
      return tree[name].metadata.usesBabelHelpersGlobal;
    });
    if (compileOpts.runtime && usesBabelHelpersGlobal)
      return getModuleSource(loader, 'babel/external-helpers')
      .then(function(source) {
        outputs.unshift(source);
      });
  })
  .then(function() {
    var usesTraceurRuntimeGlobal = modules.some(function(name) {
      return tree[name].metadata.usesTraceurRuntimeGlobal;
    });
    if (compileOpts.runtime && usesTraceurRuntimeGlobal)
      return getModuleSource(loader, 'traceur-runtime')
      .then(function(source) {
        // protect System global clobbering
        outputs.unshift("(function(){ var curSystem = typeof System != 'undefined' ? System : undefined;\n" + source + "\nSystem = curSystem; })();");
      });
  })
  // for AMD, CommonJS and global SFX outputs, add a "format " meta to support SystemJS loading
  .then(function() {
    if (compileOpts.globalName)
      outputs.unshift('"exports ' + compileOpts.globalName + '";');

    if (compileOpts.format == 'global') {
      for (var g in compileOpts.globalDeps)
        outputs.unshift('"globals.' + compileOpts.globalDeps[g] + ' ' + g + '";');
    }

    if (compileOpts.format == 'amd' || compileOpts.format == 'cjs' || compileOpts.format == 'global')
      outputs.unshift('"format ' + compileOpts.format + '";');
  })
  .then(function() {
    return outputs;
  });
}

exports.attachCompilers = function(loader) {
  Object.keys(compilerMap).forEach(function(compiler) {
    var attach = require(compilerMap[compiler]).attach;
    if (attach)
      attach(loader);
  });
};

function getModuleSource(loader, module) {
  return loader.normalize(module)
  .then(function(normalized) {
    return loader.locate({ name: normalized, metadata: {} });
  })
  .then(function(address) {
    return loader.fetch({ address: address, metadata: {} });
  })
  .then(function(fetched) {
    // allow to be a redirection module
    var redirection = fetched.toString().match(/^\s*module\.exports = require\(\"([^\"]+)\"\);\s*$/);
    if (redirection)
      return getModuleSource(loader, redirection[1]);
    return fetched;
  })
  .catch(function(err) {
    console.log('Unable to find helper module "' + module + '". Make sure it is configured in the builder.');
    throw err;
  });
}
<|endoftext|>


<commit_before>var exec, should, blog, mongoose;

mongoose  = require('mongoose');
should = require('should');
blog = require('../src/blog');

describe("blog:", function() {

  before(function() {
    if(!mongoose.connection.readyState){
      mongoose.connect('mongodb://localhost/fluiddb');
    }
  });

  after(function() {
    mongoose.disconnect();
  });

  it("creates an entry", function(next) {
    var entry = {
      title: "test1",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      data.body.should.be.eql("This is me writing my first blog post");

      blog.removeEntry(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        next();
      });
    });
  });

  it("get an entry", function(next) {
    var entry = {
      title: "test2",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      // console.log('create: ' + data);
      blog.getEntry("test2", function(err, retrievedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('get 1: ' + retrievedEntry);
        retrievedEntry[0].body.should.be.eql("This is me writing my first blog post");

        blog.removeEntry(retrievedEntry[0]._id, function(err, deletedEntry) {
          if(err) {
            console.log(err);
            return next(err);
          }
          next();
        });
      });
    });
  });

  it("delete an entry", function(next) {
    var entry = {
      title: "test3",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      blog.removeEntry(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('remove: ' + deletedEntry);
        blog.getEntry("test3", function(err, data) {
          if(err) {
            console.log(err);
            return next(err);
          }
          // console.log("get 2: " + data);
          data.should.be.empty;
          next();
        })
      });
    });
  });

  it("edit an entry", function(next) {
    var entry = {
      title: "This is a test entry 2",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      // console.log('create: ' + data);
      blog.getEntry("This is a test entry 2", function(err, retrievedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('get 1: ' + retrievedEntry);
        retrievedEntry[0].body.should.be.eql("This is me writing my first blog post");

        blog.editEntry(retrievedEntry[0]._id, {title: 'This is a modified entry 2'}, function(err, data) {

          blog.getEntry('This is a modified entry 2', function(err, data) {
            data[0].title.should.be.eql('This is a modified entry 2');
            blog.removeEntry(data[0]._id, function(err, deletedEntry) {
              if(err) {
                console.log(err);
                return next(err);
              }
              // console.log('remove: ' + deletedEntry);
              blog.getEntry("This is a modified entry", function(err, data) {
                if(err) {
                  console.log(err);
                  return next(err);
                }
                // console.log("get 2: " + data);
                data.should.be.empty;
                next();
              })
            });
          });
        });
      });
    });
  });

  it("get entries", function(next) {
    // TODO check dates are desc and pagination works
    for(var i = 1; i < 21; i++) {
      var entry = {
        title: "entry " + i,
        author: "root",
        url: "this-is-a-test-entry",
        body: "This is me writing my first blog post",
        tags: [{name: 'tag'}],
        category: 'category',
        comments: [{body: "this is a comment", date: new Date()}],
        date: new Date(new Date().getTime() + (i * (24 * 60 * 60 * 1000))),
        published: true
      };

      blog.createEntry(entry, function(err, data) {});
    }

    var limit = 5, skip = 0, j;
    setTimeout(function() {
      blog.getEntries(limit, skip, function(err, data) {
        if(err) {
          next(err);
        }
        for(j = 0; j < limit; j++) {
          // console.log(data[j].title + ' ' + data[j].id + ' ' + data[j].date);
          data[j].title.should.be.eql('entry ' + (20 - j));
        }
      });
    }, 200);

    limit = 20, skip = 0;
    setTimeout(function() {
      blog.getEntries(limit, skip, function(err, data) {
        if(err) {
          next(err);
        }
        for(j = 0; j < limit; j++) {
          blog.removeEntry(data[j]._id, function(err, removedEntry) {
            if(err) {
              next(err);
            }
          })
        }
        next();
      })
    }, 400);
  });

  it("create a category", function(next) {
    var category = {
      name: 'category 1'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      data.name.should.be.eql("category 1");

      blog.removeCategory(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        next();
      });
    });
  });

  it("get categories", function(next) {
    var category = {
      name: 'category 3'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }

      blog.getCategories(function(err, data) {
        if(err) {
          console.log(err);
          return next(err);
        }
        data.should.not.be.empty;

        for(var i = 0; i < data.length; i++) {
          if(data[i].name === "category 3") {
            blog.removeCategory(data[i]._id, function(err, data) {
              if(err) {
                next(err);
              }
              next();
            });
            break;
          }
        }
      });
    });
  });

  it("delete a category", function(next) {
    var category = {
      name: 'category 2'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }

      blog.removeCategory(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('remove: ' + deletedEntry);
        blog.getCategories(function(err, data) {
          if(err) {
            console.log(err);
            return next(err);
          }
          // console.log("get 2: " + data);
          for(var i = 0; i < data.length; i++) {
            data[i].name.should.not.be.eql('category 2');
          }
          next();
        })
      });
    });
  });
});<commit_msg>add get category test<commit_after>var exec, should, blog, mongoose;

mongoose  = require('mongoose');
should = require('should');
blog = require('../src/blog');

describe("blog:", function() {

  before(function() {
    if(!mongoose.connection.readyState){
      mongoose.connect('mongodb://localhost/fluiddb');
    }
  });

  after(function() {
    mongoose.disconnect();
  });

  it("creates an entry", function(next) {
    var entry = {
      title: "test1",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      data.body.should.be.eql("This is me writing my first blog post");

      blog.removeEntry(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        next();
      });
    });
  });

  it("get an entry", function(next) {
    var entry = {
      title: "test2",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      // console.log('create: ' + data);
      blog.getEntry("test2", function(err, retrievedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('get 1: ' + retrievedEntry);
        retrievedEntry[0].body.should.be.eql("This is me writing my first blog post");

        blog.removeEntry(retrievedEntry[0]._id, function(err, deletedEntry) {
          if(err) {
            console.log(err);
            return next(err);
          }
          next();
        });
      });
    });
  });

  it("delete an entry", function(next) {
    var entry = {
      title: "test3",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      blog.removeEntry(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('remove: ' + deletedEntry);
        blog.getEntry("test3", function(err, data) {
          if(err) {
            console.log(err);
            return next(err);
          }
          // console.log("get 2: " + data);
          data.should.be.empty;
          next();
        })
      });
    });
  });

  it("edit an entry", function(next) {
    var entry = {
      title: "This is a test entry 2",
      author: "root",
      url: "this-is-a-test-entry",
      body: "This is me writing my first blog post",
      tags: [{name: 'tag'}],
      category: 'category',
      comments: [{body: "this is a comment", date: new Date()}],
      date: new Date(),
      published: true
    };

    return blog.createEntry(entry, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      // console.log('create: ' + data);
      blog.getEntry("This is a test entry 2", function(err, retrievedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('get 1: ' + retrievedEntry);
        retrievedEntry[0].body.should.be.eql("This is me writing my first blog post");

        blog.editEntry(retrievedEntry[0]._id, {title: 'This is a modified entry 2'}, function(err, data) {

          blog.getEntry('This is a modified entry 2', function(err, data) {
            data[0].title.should.be.eql('This is a modified entry 2');
            blog.removeEntry(data[0]._id, function(err, deletedEntry) {
              if(err) {
                console.log(err);
                return next(err);
              }
              // console.log('remove: ' + deletedEntry);
              blog.getEntry("This is a modified entry", function(err, data) {
                if(err) {
                  console.log(err);
                  return next(err);
                }
                // console.log("get 2: " + data);
                data.should.be.empty;
                next();
              })
            });
          });
        });
      });
    });
  });

  it("get entries", function(next) {
    // TODO check dates are desc and pagination works
    for(var i = 1; i < 21; i++) {
      var entry = {
        title: "entry " + i,
        author: "root",
        url: "this-is-a-test-entry",
        body: "This is me writing my first blog post",
        tags: [{name: 'tag'}],
        category: 'category',
        comments: [{body: "this is a comment", date: new Date()}],
        date: new Date(new Date().getTime() + (i * (24 * 60 * 60 * 1000))),
        published: true
      };

      blog.createEntry(entry, function(err, data) {});
    }

    var limit = 5, skip = 0, j;
    setTimeout(function() {
      blog.getEntries(limit, skip, function(err, data) {
        if(err) {
          next(err);
        }
        for(j = 0; j < limit; j++) {
          // console.log(data[j].title + ' ' + data[j].id + ' ' + data[j].date);
          data[j].title.should.be.eql('entry ' + (20 - j));
        }
      });
    }, 200);

    limit = 20, skip = 0;
    setTimeout(function() {
      blog.getEntries(limit, skip, function(err, data) {
        if(err) {
          next(err);
        }
        for(j = 0; j < limit; j++) {
          blog.removeEntry(data[j]._id, function(err, removedEntry) {
            if(err) {
              next(err);
            }
          })
        }
        next();
      })
    }, 400);
  });

  it("create a category", function(next) {
    var category = {
      name: 'category 1'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }
      data.name.should.be.eql("category 1");

      blog.removeCategory(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        next();
      });
    });
  });

  it("get categories", function(next) {
    var category = {
      name: 'category 3'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }

      blog.getCategories(function(err, data) {
        if(err) {
          console.log(err);
          return next(err);
        }
        data.should.not.be.empty;

        for(var i = 0; i < data.length; i++) {
          if(data[i].name === "category 3") {
            blog.removeCategory(data[i]._id, function(err, data) {
              if(err) {
                next(err);
              }
              next();
            });
            break;
          }
        }
      });
    });
  });

  it("delete a category", function(next) {
    var category = {
      name: 'category 2'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }

      blog.removeCategory(data._id, function(err, deletedEntry) {
        if(err) {
          console.log(err);
          return next(err);
        }
        // console.log('remove: ' + deletedEntry);
        blog.getCategories(function(err, data) {
          if(err) {
            console.log(err);
            return next(err);
          }
          // console.log("get 2: " + data);
          for(var i = 0; i < data.length; i++) {
            data[i].name.should.not.be.eql('category 2');
          }
          next();
        })
      });
    });
  });

  it("gets a category", function(next) {
       var category = {
      name: 'category 4'
    };

    return blog.createCategory(category, function(err, data) {
      if(err) {
        console.log(err);
        return next(err);
      }

      blog.getCategory(data._id, function(err, data) {
        if(err) {
          next(err);
        }

        data[0].name.should.be.eql('category 4');

        blog.removeCategory(data[0]._id, function(err, deletedEntry) {
          if(err) {
            console.log(err);
            return next(err);
          }
          // console.log('remove: ' + deletedEntry);
          blog.getCategory(deletedEntry._id, function(err, data) {
            if(err) {
              console.log(err);
              return next(err);
            }
            // console.log("get 2: " + data);
            next();
          })
        });
      });
    });
  });
});<|endoftext|>


<commit_before>
exports.add_routes = function(webapp, velbus, config) {

function reply_to_get(req, res, next) {
	var addr = parseInt( req.params[0], 16 );
	var relay = parseInt( req.params[1] );
	var field = req.params[2];

	// Set up listener for the answer
	var timeout;
	var send_answer = function(msg) {
		clearTimeout(timeout);

		delete msg.data;

		if( field != undefined && field != '' ) {
			if( msg.hasOwnProperty(field) ) {
				res.send( msg[field].toString(), {'Content-Type': 'text/plain'} );
			} else {
				res.send("Unknown property", 404);
			}
		} else {
			res.send(msg);
		}
	};
	velbus.once('relay status ' + addr + '.' + relay, send_answer);
	timeout = setTimeout(function() {
			velbus.removeListener('relay status ' + addr + '.' + relay, send_answer);
			res.send("Timeout", 500);
		}, config.webapp.timeout);

	// Now send the request
	var relaybit = String.fromCharCode( 1 << (relay-1) );
	velbus.send_message(3, addr, 0, "\xfa" + relaybit );
}

webapp.get(/\/control\/relay\/([0-9a-fA-F]{2}).([1-4])(?:\/([a-zA-Z ]*))?$/, reply_to_get);

webapp.post(/\/control\/relay\/([0-9a-fA-F]{2}).([1-4])\/([a-zA-Z ]*)$/, function(req, res, next) {
	var addr = parseInt( req.params[0], 16 );
	var relay = parseInt( req.params[1] );
	var field = req.params[2];

	var relaybit = String.fromCharCode( 1 << (relay-1) );

	// We expect a single value in the body
	var value = Object.keys( req.body )
	if( value.length != 1 ) {
		res.send("Expecting single value in POST body", 400);
		return;
	}
	value = value[0];

	switch( field ) {
	case "status":
		var command;
		switch( value ) {
		case "on":
			command = "\x02" + relaybit;
			break;

		case "off":
			command = "\x01" + relaybit;
			break;

		default:
			res.send("Unknown status", 400);
			return;
		}
		velbus.send_message(0, addr, 0, command);
		break;

	default:
		res.send("Not implemented", 501);
		return;
	}

	// And fall through to the GET response
	reply_to_get(req, res, next);
});

}
<commit_msg>Switched to Buffer instead of string<commit_after>
exports.add_routes = function(webapp, velbus, config) {

function reply_to_get(req, res, next) {
	var addr = parseInt( req.params[0], 16 );
	var relay = parseInt( req.params[1] );
	var field = req.params[2];

	// Set up listener for the answer
	var timeout;
	var send_answer = function(msg) {
		clearTimeout(timeout);

		delete msg.data;

		if( field != undefined && field != '' ) {
			if( msg.hasOwnProperty(field) ) {
				res.send( msg[field].toString(), {'Content-Type': 'text/plain'} );
			} else {
				res.send("Unknown property", 404);
			}
		} else {
			res.send(msg);
		}
	};
	velbus.once('relay status ' + addr + '.' + relay, send_answer);
	timeout = setTimeout(function() {
			velbus.removeListener('relay status ' + addr + '.' + relay, send_answer);
			res.send("Timeout", 500);
		}, config.webapp.timeout);

	// Now send the request
	var relaybit = String.fromCharCode( 1 << (relay-1) );
	velbus.send_message(3, addr, 0, "\xfa" + relaybit );
}

webapp.get(/\/control\/relay\/([0-9a-fA-F]{2}).([1-4])(?:\/([a-zA-Z ]*))?$/, reply_to_get);

webapp.post(/\/control\/relay\/([0-9a-fA-F]{2}).([1-4])\/([a-zA-Z ]*)$/, function(req, res, next) {
	var addr = parseInt( req.params[0], 16 );
	var relay = parseInt( req.params[1] );
	var field = req.params[2];

	var relaybit = 1 << (relay-1);

	// We expect a single value in the body
	var value = Object.keys( req.body )
	if( value.length != 1 ) {
		res.send("Expecting single value in POST body", 400);
		return;
	}
	value = value[0];

	switch( field ) {
	case "status":
		var command;
		switch( value ) {
		case "on":
			command = new Buffer([ 0x02, relaybit ] );
			break;

		case "off":
			command = new Buffer([ 0x01, relaybit ]);
			break;

		default:
			res.send("Unknown status", 400);
			return;
		}
		velbus.send_message(0, addr, 0, command);
		break;

	default:
		res.send("Not implemented", 501);
		return;
	}

	// And fall through to the GET response
	reply_to_get(req, res, next);
});

}
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa:context');
var http = require('http');

/**
 * Prototype.
 */

module.exports = {

  /**
   * Inspect implementation.
   *
   * @return {Object}
   * @api public
   */

  inspect: function(){
    return this.toJSON();
  },

  /**
   * Return JSON representation.
   *
   * @return {Object}
   * @api public
   */

  toJSON: function(){
    return {
      request: this.request,
      response: this.response
    }
  },

  /**
   * Throw an error with `msg` and optional `status`
   * defaulting to 500. Note that these are user-level
   * errors, and the message may be exposed to the client.
   *
   *    this.throw(403)
   *    this.throw('name required', 400)
   *    this.throw(400, 'name required')
   *    this.throw('something exploded')
   *
   * @param {String|Number} msg or status
   * @param {String|Number} msg or status
   * @api public
   */

  throw: function(msg, status){
    if ('number' == typeof msg) {
      var tmp = msg;
      msg = status || http.STATUS_CODES[tmp];
      status = tmp;
    }

    var err = new Error(msg);
    err.status = status || 500;
    err.expose = true;
    throw err;
  },

  /**
   * Alias for .throw() for backwards compatibility.
   * Do not use - will be removed in the future.
   *
   * @param {String|Number} msg
   * @param {Number} status
   * @api private
   */

  error: function(msg, status){
    console.warn('ctx.error is deprecated, use ctx.throw');
    this.throw(msg, status);
  },

  /**
   * Default error handling.
   *
   * @param {Error} err
   * @api private
   */

  onerror: function(err){
    // don't do anything if there is no error.
    // this allows you to pass `this.onerror`
    // to node-style callbacks.
    if (!err) return;

    // nothing we can do here other
    // than delegate to the app-level
    // handler and log.
    if (this.headerSent || !this.socket.writable) {
      err.headerSent = true;
      this.app.emit('error', err, this);
      return;
    }

    // delegate
    this.app.emit('error', err, this);

    // force text/plain
    this.type = 'text';

    // ENOENT support
    if ('ENOENT' == err.code) err.status = 404;

    // default to 500
    err.status = err.status || 500;

    // respond
    var code = http.STATUS_CODES[err.status];
    var msg = err.expose ? err.message : code;
    this.status = err.status;
    this.res.end(msg);
  },

  /**
   * Delegate to Request#header.
   */

  get header() {
    return this.request.header;
  },

  /**
   * Delegate to Request#url.
   */

  get url() {
    return this.request.url;
  },

  /**
   * Delegate to Request#url=.
   */

  set url(val) {
    this.request.url = val;
  },

  /**
   * Delegate to Request#method.
   */

  get method() {
    return this.request.method;
  },

  /**
   * Delegate to Request#method=.
   */

  set method(val) {
    this.request.method = val;
  },

  /**
   * Delegate to Response#status.
   */

  get status() {
    return this.response.status;
  },

  /**
   * Delegate to Response#status=.
   */

  set status(val) {
    this.response.status = val;
  },

  /**
   * Delegate to Response#body.
   */

  get body() {
    return this.response.body;
  },

  /**
   * Delegate to Response#body=.
   */

  set body(val) {
    this.response.body = val;
  },

  /**
   * Delegate to Request#path.
   */

  get path() {
    return this.request.path;
  },

  /**
   * Delegate to Request#path=.
   */

  set path(val) {
    this.request.path = val;
  },

  /**
   * Delegate to Request#query.
   */

  get query() {
    return this.request.query;
  },

  /**
   * Delegate to Request#query=.
   */

  set query(val) {
    this.request.query = val;
  },

  /**
   * Delegate to Request#querystring.
   */

  get querystring() {
    return this.request.querystring;
  },

  /**
   * Delegate to Request#querystring=.
   */

  set querystring(val) {
    this.request.querystring = val;
  },

  /**
   * Delegate to Request#search.
   */

  get search() {
    return this.request.search;
  },

  /**
   * Delegate to Request#search=.
   */

  set search(val) {
    this.request.search = val;
  },

  /**
   * Delegate to Request#host.
   */

  get host() {
    return this.request.host;
  },

  /**
   * Delegate to Request#fresh.
   */

  get fresh() {
    return this.request.fresh;
  },

  /**
   * Delegate to Request#stale.
   */

  get stale() {
    return this.request.stale;
  },

  /**
   * Delegate to Request#idempotent.
   */

  get idempotent() {
    return this.request.idempotent;
  },

  /**
   * Delegate to Request#socket.
   */

  get socket() {
    return this.request.socket;
  },

  /**
   * Delegate to Request#length.
   */

  get length() {
    return this.request.length;
  },

  /**
   * Delegate to Request#length.
   */

  set length(val) {
    this.response.length = val;
  },

  /**
   * Delegate to Request#protocol.
   */

  get protocol() {
    return this.request.protocol;
  },

  /**
   * Delegate to Request#secure.
   */

  get secure() {
    return this.request.secure;
  },

  /**
   * Delegate to Request#ip.
   */

  get ip() {
    return this.request.ip;
  },

  /**
   * Delegate to Request#ips.
   */

  get ips() {
    return this.request.ips;
  },

  /**
   * Delegate to Request#subdomains.
   */

  get subdomains() {
    return this.request.subdomains;
  },

  /**
   * Delegate to Response#headerSent.
   */

  get headerSent() {
    return this.response.headerSent;
  },

  /**
   * Delegate to Response#type=.
   */

  set type(val) {
    this.response.type = val;
  },

  /**
   * Delegate to Request#type.
   */

  get type() {
    return this.request.type;
  },

  /**
   * Delegate to Response#lastModified=.
   */

  set lastModified(val) {
    this.response.lastModified = val;
  },

  /**
   * Delegate to Response#etag=.
   */

  set etag(val) {
    this.response.etag = val;
  },

  /**
   * Delegate to Request#remove().
   */

  remove: function() {
    return this.response.remove.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#accepts().
   */

  accepts: function() {
    return this.request.accepts.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsCharsets().
   */

  acceptsCharsets: function() {
    return this.request.acceptsCharsets.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsEncodings().
   */

  acceptsEncodings: function() {
    return this.request.acceptsEncodings.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsLanguages().
   */

  acceptsLanguages: function() {
    return this.request.acceptsLanguages.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#vary().
   */

  vary: function() {
    return this.response.vary.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#is().
   */

  is: function() {
    return this.request.is.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#append().
   */

  append: function() {
    return this.response.append.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#get().
   */

  get: function() {
    return this.request.get.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#set().
   */

  set: function() {
    return this.response.set.apply(this.response, arguments);
  },

  /**
   * Delegate to Response#redirect().
   */

  redirect: function() {
    return this.response.redirect.apply(this.response, arguments);
  },

  /**
   * Delegate to Response#attachment().
   */

  attachment: function() {
    return this.response.attachment.apply(this.response, arguments);
  },
};
<commit_msg>remove trailing comma<commit_after>
/**
 * Module dependencies.
 */

var debug = require('debug')('koa:context');
var http = require('http');

/**
 * Prototype.
 */

module.exports = {

  /**
   * Inspect implementation.
   *
   * @return {Object}
   * @api public
   */

  inspect: function(){
    return this.toJSON();
  },

  /**
   * Return JSON representation.
   *
   * @return {Object}
   * @api public
   */

  toJSON: function(){
    return {
      request: this.request,
      response: this.response
    }
  },

  /**
   * Throw an error with `msg` and optional `status`
   * defaulting to 500. Note that these are user-level
   * errors, and the message may be exposed to the client.
   *
   *    this.throw(403)
   *    this.throw('name required', 400)
   *    this.throw(400, 'name required')
   *    this.throw('something exploded')
   *
   * @param {String|Number} msg or status
   * @param {String|Number} msg or status
   * @api public
   */

  throw: function(msg, status){
    if ('number' == typeof msg) {
      var tmp = msg;
      msg = status || http.STATUS_CODES[tmp];
      status = tmp;
    }

    var err = new Error(msg);
    err.status = status || 500;
    err.expose = true;
    throw err;
  },

  /**
   * Alias for .throw() for backwards compatibility.
   * Do not use - will be removed in the future.
   *
   * @param {String|Number} msg
   * @param {Number} status
   * @api private
   */

  error: function(msg, status){
    console.warn('ctx.error is deprecated, use ctx.throw');
    this.throw(msg, status);
  },

  /**
   * Default error handling.
   *
   * @param {Error} err
   * @api private
   */

  onerror: function(err){
    // don't do anything if there is no error.
    // this allows you to pass `this.onerror`
    // to node-style callbacks.
    if (!err) return;

    // nothing we can do here other
    // than delegate to the app-level
    // handler and log.
    if (this.headerSent || !this.socket.writable) {
      err.headerSent = true;
      this.app.emit('error', err, this);
      return;
    }

    // delegate
    this.app.emit('error', err, this);

    // force text/plain
    this.type = 'text';

    // ENOENT support
    if ('ENOENT' == err.code) err.status = 404;

    // default to 500
    err.status = err.status || 500;

    // respond
    var code = http.STATUS_CODES[err.status];
    var msg = err.expose ? err.message : code;
    this.status = err.status;
    this.res.end(msg);
  },

  /**
   * Delegate to Request#header.
   */

  get header() {
    return this.request.header;
  },

  /**
   * Delegate to Request#url.
   */

  get url() {
    return this.request.url;
  },

  /**
   * Delegate to Request#url=.
   */

  set url(val) {
    this.request.url = val;
  },

  /**
   * Delegate to Request#method.
   */

  get method() {
    return this.request.method;
  },

  /**
   * Delegate to Request#method=.
   */

  set method(val) {
    this.request.method = val;
  },

  /**
   * Delegate to Response#status.
   */

  get status() {
    return this.response.status;
  },

  /**
   * Delegate to Response#status=.
   */

  set status(val) {
    this.response.status = val;
  },

  /**
   * Delegate to Response#body.
   */

  get body() {
    return this.response.body;
  },

  /**
   * Delegate to Response#body=.
   */

  set body(val) {
    this.response.body = val;
  },

  /**
   * Delegate to Request#path.
   */

  get path() {
    return this.request.path;
  },

  /**
   * Delegate to Request#path=.
   */

  set path(val) {
    this.request.path = val;
  },

  /**
   * Delegate to Request#query.
   */

  get query() {
    return this.request.query;
  },

  /**
   * Delegate to Request#query=.
   */

  set query(val) {
    this.request.query = val;
  },

  /**
   * Delegate to Request#querystring.
   */

  get querystring() {
    return this.request.querystring;
  },

  /**
   * Delegate to Request#querystring=.
   */

  set querystring(val) {
    this.request.querystring = val;
  },

  /**
   * Delegate to Request#search.
   */

  get search() {
    return this.request.search;
  },

  /**
   * Delegate to Request#search=.
   */

  set search(val) {
    this.request.search = val;
  },

  /**
   * Delegate to Request#host.
   */

  get host() {
    return this.request.host;
  },

  /**
   * Delegate to Request#fresh.
   */

  get fresh() {
    return this.request.fresh;
  },

  /**
   * Delegate to Request#stale.
   */

  get stale() {
    return this.request.stale;
  },

  /**
   * Delegate to Request#idempotent.
   */

  get idempotent() {
    return this.request.idempotent;
  },

  /**
   * Delegate to Request#socket.
   */

  get socket() {
    return this.request.socket;
  },

  /**
   * Delegate to Request#length.
   */

  get length() {
    return this.request.length;
  },

  /**
   * Delegate to Request#length.
   */

  set length(val) {
    this.response.length = val;
  },

  /**
   * Delegate to Request#protocol.
   */

  get protocol() {
    return this.request.protocol;
  },

  /**
   * Delegate to Request#secure.
   */

  get secure() {
    return this.request.secure;
  },

  /**
   * Delegate to Request#ip.
   */

  get ip() {
    return this.request.ip;
  },

  /**
   * Delegate to Request#ips.
   */

  get ips() {
    return this.request.ips;
  },

  /**
   * Delegate to Request#subdomains.
   */

  get subdomains() {
    return this.request.subdomains;
  },

  /**
   * Delegate to Response#headerSent.
   */

  get headerSent() {
    return this.response.headerSent;
  },

  /**
   * Delegate to Response#type=.
   */

  set type(val) {
    this.response.type = val;
  },

  /**
   * Delegate to Request#type.
   */

  get type() {
    return this.request.type;
  },

  /**
   * Delegate to Response#lastModified=.
   */

  set lastModified(val) {
    this.response.lastModified = val;
  },

  /**
   * Delegate to Response#etag=.
   */

  set etag(val) {
    this.response.etag = val;
  },

  /**
   * Delegate to Request#remove().
   */

  remove: function() {
    return this.response.remove.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#accepts().
   */

  accepts: function() {
    return this.request.accepts.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsCharsets().
   */

  acceptsCharsets: function() {
    return this.request.acceptsCharsets.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsEncodings().
   */

  acceptsEncodings: function() {
    return this.request.acceptsEncodings.apply(this.request, arguments);
  },

  /**
   * Delegate to Request#acceptsLanguages().
   */

  acceptsLanguages: function() {
    return this.request.acceptsLanguages.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#vary().
   */

  vary: function() {
    return this.response.vary.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#is().
   */

  is: function() {
    return this.request.is.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#append().
   */

  append: function() {
    return this.response.append.apply(this.response, arguments);
  },

  /**
   * Delegate to Request#get().
   */

  get: function() {
    return this.request.get.apply(this.request, arguments);
  },

  /**
   * Delegate to Response#set().
   */

  set: function() {
    return this.response.set.apply(this.response, arguments);
  },

  /**
   * Delegate to Response#redirect().
   */

  redirect: function() {
    return this.response.redirect.apply(this.response, arguments);
  },

  /**
   * Delegate to Response#attachment().
   */

  attachment: function() {
    return this.response.attachment.apply(this.response, arguments);
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

var async = require('async'),
    csv = require('fast-csv'),
    fs = require('fs'),
    _ = require('underscore'),
    mongoose = require('mongoose'),
    Hours = require('../server/models').Hours,
    Project = require('../server/models').Project,
    settings = require('../server/settings');

var argv = require('yargs')
    .default("file", "export.csv")
    .example('$0 output_file.csv', 'export data to output_file.csv')
    .help('h')
    .alias('h', 'help')
    .argv;

mongoose.connect(settings.mongo.servers.join(','), {replSet: {rs_name: settings.mongo.replset}});

async.waterfall([
    function (cb) {
        Project.find().select("name client").populate("client", "name").exec(function (err, projects) {
            if (err) {
                return cb(err);
            }
            var p = {};
            _.each(projects, function (project) {
                p[project._id] = project.client.name + "/" + project.name
            })
            cb(null, p);
        });
    },
    function (projects, cb) {
        Hours.aggregate([
            {
                $project: {
                    project: "$project",
                    duration: "$duration",
                    ymd: {$dateToString: {format: "%Y-%m", date: "$date"}}
                }
            }, {
                $group: {_id: {
                    ymd: "$ymd",
                    project: "$project",
                    duration: {$sum: "$duration"}
                }}
            }, {
                $group: {
                    _id: "$_id.ymd",
                    projects: {"$push": {"project": "$_id.project", "duration": "$_id.duration"}},
                    duration: {"$sum": "$_id.duration"}}
            }, {
                $sort: {
                    _id: 1
                }
            }
        ]).exec(function (err, results) {
            if (err) throw err;

            var data = [];
            var headers = ["date", "duration"];
            _.each(results, function (line) {
                var o = {};
                o.date = line._id;
                o.duration = line.duration;
                _.each(line.projects, function (project) {
                    o[projects[project.project]] = project.duration;
                    headers.push(projects[project.project]);
                });
                data.push(o);
            });
            cb(null, {data: data, headers: _.uniq(headers)});
        });
    }
], function (err, res) {
    if (err) {
        console.error(err);
    }

    csv.writeToPath(argv.file, res.data, {headers: res.headers}).on("finish", function () {
        process.exit(0);
    });

});
<commit_msg>Sum should not be part of id field<commit_after>#!/usr/bin/env node

var async = require('async'),
    csv = require('fast-csv'),
    fs = require('fs'),
    _ = require('underscore'),
    mongoose = require('mongoose'),
    Hours = require('../server/models').Hours,
    Project = require('../server/models').Project,
    settings = require('../server/settings');

var argv = require('yargs')
    .default("file", "export.csv")
    .example('$0 output_file.csv', 'export data to output_file.csv')
    .help('h')
    .alias('h', 'help')
    .argv;

mongoose.connect(settings.mongo.servers.join(','), {replSet: {rs_name: settings.mongo.replset}});

async.waterfall([
    function (cb) {
        Project.find().select("name client").populate("client", "name").exec(function (err, projects) {
            if (err) {
                return cb(err);
            }
            var p = {};
            _.each(projects, function (project) {
                p[project._id] = project.client.name + "/" + project.name
            })
            cb(null, p);
        });
    },
    function (projects, cb) {
        Hours.aggregate([
            {
                $project: {
                    project: "$project",
                    duration: "$duration",
                    ymd: {$dateToString: {format: "%Y-%m", date: "$date"}}
                }
            }, {
                $group: {
                    _id: {
                        ymd: "$ymd",
                        project: "$project",
                    },
                    duration: {"$sum": "$duration"}
                }
            }, {
                $group: {
                    _id: "$_id.ymd",
                    projects: {"$push": {"project": "$_id.project", "duration": "$duration"}},
                    duration: {"$sum": "$duration"}
                }
            }, {
                $sort: {
                    _id: 1
                }
            }
        ]).exec(function (err, results) {
            if (err) throw err;

            var data = [];
            var headers = ["date", "duration"];
            _.each(results, function (line) {
                var o = {};
                o.date = line._id;
                o.duration = line.duration;
                _.each(line.projects, function (project) {
                    o[projects[project.project]] = project.duration;
                    headers.push(projects[project.project]);
                });
                data.push(o);
            });
            cb(null, {data: data, headers: _.uniq(headers)});
        });
    }
], function (err, res) {
    if (err) {
        console.error(err);
    }

    csv.writeToPath(argv.file, res.data, {headers: res.headers}).on("finish", function () {
        process.exit(0);
    });

});
<|endoftext|>