From c251a3d357b64137f5825ccf211d33be6bee3f21 Mon Sep 17 00:00:00 2001 From: Mike Botsko Date: Sat, 10 Oct 2015 11:30:17 -0700 Subject: [PATCH] Linting, .editorconfig, prep for travis-ci, code cleanup --- .editorconfig | 13 ++++++ .eslintrc | 114 +++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 1 + .travis.yml | 4 ++ index.js | 88 ++++++++++++++++++++-------------- package.json | 13 +++--- test/helpers.js | 12 +++-- test/index.spec.js | 9 ++-- 8 files changed, 203 insertions(+), 51 deletions(-) create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .travis.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c308ed0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..f2ce0b9 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,114 @@ +{ + "env": { + "amd": true, + "browser": true, + "node": true, + "mocha": true + }, + "rules": { + "accessor-pairs": 2, + "array-bracket-spacing": [2, "never"], + "block-scoped-var": 2, + "block-spacing": [2, "always"], + "brace-style": [2, "1tbs"], + "callback-return": 2, + "camelcase": 2, + "comma-dangle": [2, "never"], + "comma-spacing": [2, { + "before": false, + "after": true + }], + "comma-style": [2, "last"], + "curly": [ 2, "all" ], + "default-case": 2, + "dot-location": [2, "property"], + "dot-notation": 2, + "eol-last": 2, + "eqeqeq": 2, + "guard-for-in": 2, + "indent": 2, + "jsx-quotes": [ 1, "prefer-single" ], + "key-spacing": [2, { + "beforeColon": false, + "afterColon": true + }], + "lines-around-comment": [2, { + "beforeBlockComment": true, + "beforeLineComment": true, + "allowBlockStart": true + }], + "linebreak-style": [2, "unix"], + "new-cap": 2, + "new-parens": 2, + "no-alert": 2, + "no-array-constructor": 2, + "no-cond-assign": 2, + "no-dupe-args": 2, + "no-dupe-keys": 2, + "no-empty": 2, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implicit-coercion": 2, + "no-implied-eval": 2, + "no-inline-comments": 2, + "no-inner-declarations": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-lone-blocks": 2, + "no-lonely-if": 2, + "no-mixed-requires": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multiple-empty-lines": 2, + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-nested-ternary": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-path-concat": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-sparse-arrays": 2, + "no-trailing-spaces": 2, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": 2, + "no-unreachable": 2, + "no-unused-vars": 2, + "no-use-before-define": 2, + "no-useless-call": 2, + "no-void": 2, + "no-with": 2, + "object-curly-spacing": [2, "always"], + "operator-assignment": [2, "always"], + "operator-linebreak": [2, "after"], + "padded-blocks": [2, "never"], + "quote-props": [2, "as-needed"], + "quotes": [2, "single"], + "radix": 2, + "semi": [2, "always"], + "space-after-keywords": 2, + "space-before-blocks": [2, "always"], + "space-before-function-paren": [2, "never"], + "space-before-keywords": [2, "always"], + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": 2, + "spaced-comment": 2, + "use-isnan": 2, + "valid-jsdoc": 2, + "valid-typeof": 2, + "wrap-iife": 2, + "yoda": [ 2, "never" ] + } +} diff --git a/.gitignore b/.gitignore index 699c4eb..38a8f8e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ Desktop.ini $RECYCLE.BIN/ node_modules/ .tmp +npm-debug.log diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..dc404e6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,4 @@ +language: node_js +node_js: + - 0.12 + - 4.0.0 diff --git a/index.js b/index.js index faa9366..2e07c90 100644 --- a/index.js +++ b/index.js @@ -1,45 +1,61 @@ -module.exports = function (schema, options) { - var message = 'Error, expected `{PATH}` to be unique. Value: `{VALUE}`'; - if (options && options.message) { - message = options.message; - } - schema.eachPath(function (path, schemaType) { - if (schemaTypeHasUniqueIndex(schemaType)) { - var validator = buildUniqueValidator(path); - message = buildMessage(schemaType.options.unique, message); - schemaType.validate(validator, message); +'use strict'; + +/** + * Builds query to find any duplicate records. + * + * @param {string} path Path name. + * @param {string} value Value to search for. + * @param {object} id ObjectID of self + * @return {object} MongoDB query object + */ +var buildQuery = function buildQuery(path, value, id) { + // Build a base query, ensure duplicates aren't ourself + var query = { $and: [{ + _id: { + $ne: id } - }); -}; + }] }; -function buildMessage(uniqueOption, message){ - return typeof uniqueOption === 'string' ? uniqueOption : message; -} + // Match target path + var target = {}; + target[path] = value; + query.$and.push(target); -function schemaTypeHasUniqueIndex(schemaType) { - return schemaType._index && schemaType._index.unique; -} + return query; +}; -function buildUniqueValidator(path) { - return function (value, respond) { +/** + * Mongoose validator to be executed per-path. + * + * @param {string} path Path name. + * @return {function} Mongoose validation function + */ +var buildValidator = function buildValidator(path) { + return function(value, respond) { + // Awaiting more official way to obtain reference to model. + // https://github.com/Automattic/mongoose/issues/3430 var model = this.model(this.constructor.modelName); var query = buildQuery(path, value, this._id); - var callback = buildValidationCallback(respond); - model.findOne(query, callback); + model.findOne(query, function(err, document) { + respond(!document); + }); }; -} +}; -function buildQuery(field, value, id) { - var query = { $and: [] }; - var target = {}; - target[field] = value; - query.$and.push({ _id: { $ne: id } }); - query.$and.push(target); - return query; -} +// Export the mongoose plugin +module.exports = function(schema, options) { + var message = 'Error, expected `{PATH}` to be unique. Value: `{VALUE}`'; + if (options && options.message) { + message = options.message; + } -function buildValidationCallback(respond) { - return function (err, document) { - respond(!document); - }; -} + schema.eachPath(function(path, schemaType) { + if (schemaType._index && schemaType._index.unique) { + if (typeof schemaType.options.unique === 'string') { + message = schemaType.options.unique; + } + + schemaType.validate(buildValidator(path), message); + } + }); +}; diff --git a/package.json b/package.json index 4074d4b..f0c4943 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "mongoose-unique-validator is a plugin which adds pre-save validation for unique fields within a Mongoose schema.", "main": "index.js", "scripts": { - "test": "mocha test" + "test": "mocha test && ./node_modules/eslint/bin/eslint.js index.js test", + "lint": "eslint index.js test" }, "keywords": [ "mongoose", @@ -16,12 +17,10 @@ "email": "haswell00@gmail.com", "url": "http://blakehaswell.com/" }, - "contributors": [ - { - "name": "Mike Botsko", - "email": "botsko@gmail.com" - } - ], + "contributors": [{ + "name": "Mike Botsko", + "email": "botsko@gmail.com" + }], "license": "MIT", "repository": { "type": "git", diff --git a/test/helpers.js b/test/helpers.js index f851d75..7de3552 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,9 +1,11 @@ +'use strict'; + var mongoose = require('mongoose'); // Helper methods/objects for tests module.exports = { createUserSchema: function() { - return mongoose.Schema({ + return new mongoose.Schema({ username: { type: String, unique: true @@ -20,7 +22,7 @@ module.exports = { }, createCustomUserSchema: function() { - return mongoose.Schema({ + return new mongoose.Schema({ username: { type: String, unique: 'Username is already used.' @@ -37,9 +39,9 @@ module.exports = { }, USERS: [{ - username: 'JohnSmith', - email: 'john.smith@gmail.com', - password: 'j0hnNYb0i' + username: 'JohnSmith', + email: 'john.smith@gmail.com', + password: 'j0hnNYb0i' }, { username: 'Robert Miller', email: 'bob@robertmiller.com', diff --git a/test/index.spec.js b/test/index.spec.js index f0a4648..3d56316 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1,3 +1,5 @@ +'use strict'; + var helpers = require('./helpers'); var expect = require('chai').expect; var mongoose = require('mongoose'); @@ -28,7 +30,7 @@ describe('Mongoose Unique Validator', function() { }); }); - describe('Default Configuration', function () { + describe('Default Configuration', function() { var User = mongoose.model('User', helpers.createUserSchema().plugin(uniqueValidator)); models.push(User); @@ -38,6 +40,7 @@ describe('Mongoose Unique Validator', function() { promise.then(function() { // Try saving a duplicate new User(helpers.USERS[0]).save().catch(function(err) { + // console.log(err); expect(err.errors.username.message).to.equal('Error, expected `username` to be unique. Value: `JohnSmith`'); expect(err.errors.username.properties.type).to.equal('user defined'); expect(err.errors.username.properties.path).to.equal('username'); @@ -65,7 +68,7 @@ describe('Mongoose Unique Validator', function() { }); }); - describe('Custom Configuration', function () { + describe('Custom Configuration', function() { var User = mongoose.model('UserErrorMessage', helpers.createUserSchema().plugin(uniqueValidator, { message: 'Path: {PATH}, value: {VALUE}, type: {TYPE}' })); @@ -87,7 +90,7 @@ describe('Mongoose Unique Validator', function() { }); }); - describe('Configuration via Schema', function () { + describe('Configuration via Schema', function() { var User = mongoose.model('UserErrorCustomMessage', helpers.createCustomUserSchema().plugin(uniqueValidator)); models.push(User);