{"size": 11595, "ext": "js", "max_stars_repo_path": "src/couchConfig.js", "max_stars_repo_name": "BlueSlug/couch-config", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "src/couchConfig.js", "max_issues_repo_name": "BlueSlug/couch-config", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "src/couchConfig.js", "max_forks_repo_name": "BlueSlug/couch-config", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 36.234375, "max_line_length": 237, "alphanum_fraction": 0.5564467443}
"use strict";

var fluid = require("infusion");
var isEqual = require("underscore").isEqual;

var sjrk = fluid.registerNamespace("sjrk");

fluid.defaults("sjrk.server.couchConfig.base", {
    gradeNames: ["fluid.component"],
    dbConfig: {
        couchURL: "http://localhost:5984"
    }
});

fluid.defaults("sjrk.server.couchConfig.db", {
    gradeNames: ["sjrk.server.couchConfig.base"],
    dbConfig: {
        // dbName: "targetDB",
    },
    events: {
        // Fired after the component confirms the target DB exists;
        // necessary for sequencing document-related updates
        onDBExists: null
    },
    invokers: {
        ensureDBExists: {
            funcName: "sjrk.server.couchConfig.db.ensureDBExists",
            args: ["{that}.options.dbConfig.couchURL", "{that}.options.dbConfig.dbName", "{that}.events.onDBExists"]
        }
    }
});

sjrk.server.couchConfig.db.ensureDBExists = function (couchURL, dbName, completionEvent) {
    console.log("Making sure DB " + dbName + " exists in Couch instance at " + couchURL);
    var nano = require("nano")(couchURL);
    nano.db.get(dbName, function (err, body) {
        if (!err) {
            console.log("DB " + dbName + " exists");
            completionEvent.fire();
        } else {
            if (err.statusCode === 404) {
                console.log("DB does not exist, trying to create");
                nano.db.create(dbName, function (err, body) {
                    if (!err) {
                        console.log("DB " + dbName + " created");
                        completionEvent.fire();
                    } else {
                        console.log("DB " + dbName + " could not be created");
                        console.log(err, body);
                    }
                });
            } else {
                console.log("Could not get information about DB " + dbName);
                console.log(err, body);
            }

        }
    });
};

fluid.defaults("sjrk.server.couchConfig.documents", {
    dbConfig: {
        // dbName: "targetDB",
    },
    gradeNames: ["sjrk.server.couchConfig.base"],
    // Ensure one or more documents exist; key will be used as the document _id
    dbDocuments: {
        // "test1": {
        //     "message": "Hello, World!",
        //     "tags": ["Hello", "World", "test"]
        // }
    },
    events: {
        // Fired after the documents are updated
        // necessary for making sure documents aren't pushed before a
        // validation function is in place
        onDocsUpdated: null
    },
    invokers: {
        updateDocuments: {
            funcName: "sjrk.server.couchConfig.documents.updateDocuments",
            args: ["{that}.options.dbDocuments", "{that}.options.dbConfig.couchURL", "{that}.options.dbConfig.dbName", "{that}.events.onDocsUpdated"]
        }
    }
});

sjrk.server.couchConfig.documents.updateDocuments = function (documents, couchURL, dbName, completionEvent) {
    if (isEqual(documents, {})) {
        console.log("No documents to update");
        return;
    }

    console.log("Updating documents at for DB " + dbName + " in Couch instance at " + couchURL);

    var nano = require("nano")(couchURL);
    nano.use(dbName);

    var targetDB = nano.use(dbName);

    fluid.each(documents, function (doc, id) {

        targetDB.get(id, function (err, body) {
            if (!err) {
                console.log("Document " + id + " found");

                var existingDocValues = fluid.censorKeys(body, ["_id", "_rev"]);
                var docValuesEqual = isEqual(doc, existingDocValues);
                if (docValuesEqual) {
                    console.log("Document values of " + id + " are equivalent, not updating to avoid needless revisioning");
                    completionEvent.fire();
                    return;
                }

                doc._rev = body._rev;
                targetDB.insert(doc, id, function (err, body) {
                    if (!err) {
                        console.log("Update of document " + id + " inserted");
                        console.log(body);
                        completionEvent.fire();
                    }
                    if (err) {
                        console.log("Update of document " + id + " could not be inserted");
                        console.log(err, body);
                    }
                });
            }
            if (err) {
                console.log("Document " + id + " not found, creating");
                targetDB.insert(doc, id, function (err, body) {
                    if (!err) {
                        console.log("Document " + id + " inserted");
                        console.log(body);
                        completionEvent.fire();
                    }
                    if (err) {
                        console.log("Document " + id + " could not be inserted");
                        console.log(err, body);
                    }
                });
            }
        });
    });
};

fluid.defaults("sjrk.server.couchConfig.designDocument", {
    gradeNames: ["sjrk.server.couchConfig.base"],
    events: {
        // Fired after the design document is updated
        // necessary for making sure documents aren't pushed before a
        // validation function is in place
        onDesignDocUpdated: null
    },
    invokers: {
        updateDesignDoc: {
            funcName: "sjrk.server.couchConfig.designDocument.updateDesignDoc",
            args: ["{that}.options.dbViews", "{that}.options.dbValidate.validateFunction", "{that}.options.dbConfig.couchURL", "{that}.options.dbConfig.dbName", "{that}.options.dbConfig.designDocName", "{that}.events.onDesignDocUpdated"]
        }
    },
    dbConfig: {
        // dbName: "targetDB",
        // designDocName: "views"
    },
    // map / reduce can be a function reference or a
    // function name as string. CouchDB's internal
    // reduce functions (_sum, _count, and _stats) can also
    // be used by name as strings in the reduce key
    dbViews: {
        // count: {
        //     map: "sjrk.server.couchConfig.countMapFunction",
        //     reduce: "_count"
        // }
    },
    // Supply a validation function to be mapped to validate_doc_update in the
    // design document
    dbValidate: {
        // validateFunction: "sjrk.server.couchConfig.validateFunction"
    }
});

sjrk.server.couchConfig.designDocument.generateViews = function (viewsObj) {
    var transformedView = fluid.transform(viewsObj, function (desiredView) {
        var transformedFunction = fluid.transform(desiredView, function (viewFunc, funcKey) {
            // The internal CouchDB reduce functions
            if (funcKey === "reduce" && (viewFunc === "_count" || viewFunc === "_sum" || viewFunc === "_stats")) {
                return viewFunc;
            }
            // Direct function references
            if (typeof viewFunc === "function") {
                return viewFunc.toString();
            }
            // Resolve funcNames using fluid.getGlobalValue
            if (typeof viewFunc === "string") {
                var namedFunc = fluid.getGlobalValue(viewFunc);
                return namedFunc.toString();
            }
        });
        return transformedFunction;
    });
    return transformedView;
};

// Generates a base design document
sjrk.server.couchConfig.designDocument.getBaseDesignDocument = function (designDocName) {
    return {
        _id: "_design/" + designDocName,
        views: {},
        language: "javascript"
    };
};

sjrk.server.couchConfig.designDocument.updateDesignDoc = function (viewsObj, validateFunction, couchURL, dbName, designDocName, completionEvent) {

    var designDocObj = {};

    var generatedViews = sjrk.server.couchConfig.designDocument.generateViews(viewsObj);

    if (!isEqual(generatedViews, {})) {
        designDocObj.views = generatedViews;
    }

    if (validateFunction) {
        // Direct function references
        if (typeof validateFunction === "function") {
            designDocObj.validate_doc_update = validateFunction.toString();
        }
        // Resolve funcNames using fluid.getGlobalValue
        if (typeof validateFunction === "string") {
            var namedFunc = fluid.getGlobalValue(validateFunction);
            designDocObj.validate_doc_update = namedFunc.toString();
        }
    }

    if (isEqual(designDocObj, {})) {
        console.log("No design document elements");
        return;
    }

    console.log(fluid.stringTemplate("Updating design document at %couchURL/%dbName/_design/%designDocName with defined views", {couchURL: couchURL, dbName: dbName, designDocName: designDocName}));

    var designDoc;

    var nano = require("nano")(couchURL);

    var targetDB = nano.use(dbName);

    var designDocId = "_design/" + designDocName;

    targetDB.get(designDocId, function (err, body) {
        // Design document exists
        if (!err) {
            console.log("Existing design document found");

            designDoc = body;
            var originaldesignDoc = fluid.copy(designDoc);

            fluid.each(designDocObj, function (designDocItem, designDocItemKey) {
                designDoc[designDocItemKey] = designDocItem;
            });

            var designDocChanged = !isEqual(originaldesignDoc, designDoc);

            if (designDocChanged) {
                targetDB.insert(designDoc, designDocId, function (err, body) {
                    console.log("Design doc has been changed, attempting to update");
                    if (!err) {
                        console.log("Design doc updated successfully");
                        console.log(body);
                        completionEvent.fire();
                    } else {
                        console.log("Error in updating design doc");
                        console.log(err, body);
                    }
                });
            } else {
                console.log("Design document unchanged from existing in CouchDB, not updating");
                completionEvent.fire();
            }
        // Design document does not exist
        } else {
            console.log("Design document not found, creating");
            designDoc = sjrk.server.couchConfig.designDocument.getBaseDesignDocument(designDocName);

            fluid.each(designDocObj, function (designDocItem, designDocItemKey) {
                designDoc[designDocItemKey] = designDocItem;
            });

            targetDB.insert(designDoc, designDocId, function (err, body) {
                if (!err) {
                    console.log("Design doc created successfully");
                    console.log(body);
                    completionEvent.fire();
                } else {
                    console.log(err, body);
                    console.log("Error in creating design document");
                }
            });
        }
    });

};

// Convenience grade that calls all the configuration functions at instantiation,
// in an appropriate order - intended to set up an application's initial
// configuration in one go
fluid.defaults("sjrk.server.couchConfig.auto", {
    gradeNames: ["sjrk.server.couchConfig.db", "sjrk.server.couchConfig.documents", "sjrk.server.couchConfig.designDocument"],
    listeners: {
        "onCreate.ensureDBExists": {
            func: "{that}.ensureDBExists"
        },
        "onDBExists.updateDesignDoc": {
            func: "{that}.updateDesignDoc"
        },
        "onDesignDocUpdated.updateDocuments": {
            func: "{that}.updateDocuments"
        }

    }
});
