Source: interceptors/autofields.js

/**
 * Interception module for creating and updating fields automatically.
 * That is, fields that don't have to be explicitly set on a document.
 *
 * The folowing fields are supported:
 *
 * | Type               | Default field name    | Description                                                               |
 * |--------------------|-----------------------|---------------------------------------------------------------------------|
 * | Created At         | _createdAt            | Date and time when the item was first added to the collection.            |
 * | Created By         | _createdBy            | The identifier of the user that first added the item to the collection.   |
 * | Last Modified      | _lastModified         | Date and time of the last update of the item.                             |
 * | Modified By        | _modifiedBy           | The identifier of the user that made the last update of the item.         |
 * | Version            | _version              | The number of times the item was successfully updated.                    |
 *
 * For more details on using interceptors please see the {@tutorial interceptors} tutorial.
 *
 * @example
 *  var entree      = require("entree"),
 *      autofields  = entree.resolveInterceptor("autofields");
 *
 * entree.posts.use(autofields.interception({ createdAt: true, version: "_myUpdateCountField" }));
 *
 * @module autofields
 */

"use strict";

var object  = require("../utils/object");

function tryToFind(val) {
    var type = typeof val;
    if (type === "string" || type === "number") {
        return val;
    }

    return val._id || val.username || val.email || val.name;
}

function getUserIdentity(context) {
    if (context) {
        var user = tryToFind(context);
        if (user) {
            return user;
        }
        return tryToFind(context.user);
    }
    return null;
}

function updateAutoFields(item, context, opts, upsert) {
    var identity, date;

    if (object.validateKeys(item) === "fields") {
        if (opts.createdAt) {
            item[opts.createdAt] = date = new Date();
        }

        if (opts.createdBy) {
            item[opts.createdBy] = identity = getUserIdentity(context);
        }

        if (opts.version) {
            item[opts.version] = 1;
        }

        if (opts.lastModified) {
            item[opts.lastModified] = date || new Date();
        }

        if (opts.modifiedBy) {
            item[opts.modifiedBy] = identity || getUserIdentity(context);
        }
    } else {
        if (upsert && (opts.createdAt || opts.createdBy)) {
            if (!item.$setOnInsert) {
                item.$setOnInsert = {};
            }

            if (opts.createdAt) {
                item.$setOnInsert[opts.createdAt] = date = new Date();
            }

            if (opts.createdBy) {
                item.$setOnInsert[opts.createdBy] = identity = getUserIdentity(context);
            }
        }

        if (opts.version) {
            if (!item.$inc) {
                item.$inc = {};
            }
            item.$inc[opts.version] = 1;
        }

        if (opts.lastModified || opts.modifiedBy) {
            if (!item.$set) {
                item.$set = {};
            }

            if (opts.lastModified) {
                item.$set[opts.lastModified] = date || new Date();
            }

            if (opts.modifiedBy) {
                item.$set[opts.modifiedBy] = identity || getUserIdentity(context);
            }
        }
    }
}

function wrapCursor(cursor, context, opts) {
    var orgFn = cursor.update;
    cursor.update = function (data, callback) {
        updateAutoFields(data, context, opts);
        orgFn.call(cursor, data, callback);
    };
    return cursor;
}

/**
 * Configures and returns an interceptor function.
 *
 * Options
 *
 * - `createdAt` {(boolean|string)=} - if true, defaults to "_createdAt".
 * - `createdBy` {(boolean|string)=} - if true, defaults to "_createdBy".
 * - `lastModified` {(boolean|string)=} - if true, defaults to "_lastModified".
 * - `modifiedBy` {(boolean|string)=} - if true, defaults to "_modifiedBy".
 * - `version` {(boolean|string)=} - if true, defaults to "_version".
 *
 * If any of the options is a string, then the value is used for field name for that option.
 * If options are omitted, all supported fields will be configured with their default names.
 *
 * @param {object=} opts - Autofields options, defines which auto-fileds should be handled and their names.
 * @return {function}
 */
exports.interception = function (opts) {
    if (!opts) {
        opts = {
            createdAt       : "_createdAt",
            createdBy       : "_createdBy",
            lastModified    : "_lastModified",
            modifiedBy      : "_modifiedBy",
            version         : "_version"
        };
    } else {
        if (opts.createdAt && typeof opts.createdAt !== "string") {
            opts.createdAt = "_createdAt";
        }
        if (opts.createdBy && typeof opts.createdBy !== "string") {
            opts.createdBy = "_createdBy";
        }
        if (opts.lastModified && typeof opts.lastModified !== "string") {
            opts.lastModified = "_lastModified";
        }
        if (opts.modifiedBy && typeof opts.modifiedBy !== "string") {
            opts.modifiedBy = "_modifiedBy";
        }
        if (opts.version && typeof opts.version !== "string") {
            opts.version = "_version";
        }
    }

    return function (action, context, item, next, out) {
        try {
            switch (action) {
            case "_insert":
            case "_update":
                updateAutoFields(item, context, opts);
                break;
            case "_upsert":
                updateAutoFields(item, context, opts, true);
                break;
            case "_select":
                if (out) {
                    next(item, function (err, cur) {
                        if (cur) {
                            wrapCursor(cur, context, opts);
                        }
                        out(err, cur);
                    });
                } else {
                    return wrapCursor(next(item), context, opts);
                }
                return;
            }
            next(item, out);
        } catch (err) {
            if (out) {
                return out(err);
            }
            throw err;
        }
    };
};
EntreeJS Copyright © 2013-2014 The contributors to the EntreeJS project.
Documentation generated by JSDoc 3.2.2 on Mon May 26 2014 17:43:41 GMT+0300 (EEST) using the DocStrap template.