Repository: TechnologyAdvice/obey (js)

Issue:
(breaking) change `requireIf`(Not) to `requiredIf`(Not)
Since the property name is `required` (not `require`) it makes more sense if the property name and API uses the same convention for `requiredIf` and `requiredIfNot`.

Pull request description:
Aliases
Closes #62 

Patch:
diff --git a/README.md b/README.md
index 854b477..2ec0246 100644
--- a/README.md
+++ b/README.md
@@ -71,8 +71,8 @@ const userModel = obey.model({
   lname: { type: 'string', description: 'Last Name', empty: true },
   suffix: { type: 'string', allowNull: true },
   phone: { type: 'phone:numeric', min: 7, max: 10 },
-  phoneType: { type: 'string', requireIf: 'phone' },
-  carrier: { type: 'string', requireIf: { phoneType: 'mobile' }},
+  phoneType: { type: 'string', requiredIf: 'phone' },
+  carrier: { type: 'string', requiredIf: { phoneType: 'mobile' }},
   // Array
   labels: { type: 'array', values: {
     type: 'object', keys: {
@@ -160,8 +160,8 @@ When setting definitions for rules or model properties, the following are suppor
 * `min`: The minimum character length for a string, lowest number, or minimum items in array
 * `max`: The maximum character length for a string, highest number, or maximum items in array
 * `required`: Enforces the value cannot be `undefined` during validation (default `false`)
-* `requireIf`: Enforces the value cannot be `undefined` if a value exists or matches a given value (`{ propertyName: 'requiredValue' }`)
-* `requireIfNot`: Enforces the value cannot be `undefined` if a value _does not_ exist or match a given value (`{ propertyName: 'requiredValue' }`)
+* `requiredIf`: Enforces the value cannot be `undefined` if a value exists or matches a given value (`{ propertyName: 'requiredValue' }`)
+* `requiredIfNot`: Enforces the value cannot be `undefined` if a value _does not_ exist or match a given value (`{ propertyName: 'requiredValue' }`)
 * `equalTo`: Enforces the value to be the same as the corresponding field
 * `allow`: Object, array or single value representing allowed value(s), see [Allow](#allow)
 * `allowNull`: Accepts a null value or processes specified type
diff --git a/package.json b/package.json
index e20fa75..2b534c0 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,8 @@
   "dependencies": {
     "bluebird": "^3.3.4",
     "dot-object": "^1.5.4",
-    "lodash": "^4.6.1"
+    "lodash": "^4.6.1",
+    "snyk": "^1.23.3"
   },
   "pre-commit": [
     "test"
diff --git a/src/lib/validators.js b/src/lib/validators.js
index 3a26c73..31f3739 100644
--- a/src/lib/validators.js
+++ b/src/lib/validators.js
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2015 TechnologyAdvice
  */
+/* eslint no-console: 0 */
 import dot from 'dot-object'
 
 const validators = {
@@ -69,7 +70,7 @@ const validators = {
   },
 
   /**
-   * Validator requireIf method, used by model
+   * Validator requiredIf method, used by model
    * @param {Object} def The property configuration
    * @param {*} value The value being validated
    * @param {string} key The key name of the property
@@ -77,9 +78,9 @@ const validators = {
    * to which any additional error objects will be added
    * @param {Object} data The full initial data object
    */
-  requireIf: function(def, value, key, errors, data) {
-    const type = 'requireIf'
-    const sub = def.requireIf
+  requiredIf: function(def, value, key, errors, data) {
+    const type = 'requiredIf'
+    const sub = def.requiredIf
     if (typeof sub === 'object') {
       const field = Object.keys(sub)[0]
       if (dot.pick(field, data) === sub[field] && value === undefined) {
@@ -89,9 +90,18 @@ const validators = {
       errors.push({ type, sub, key, value, message: `Value required because '${sub}' exists` })
     }
   },
+  /**
+   * Alias for requiredIf
+   */
+  requireIf: function(def, value, key, errors, data) {
+    console.log('-----\nObey Warning: `requireIf` should be `requiredIf`\n-----')
+    def.requiredIf = def.requireIf
+    delete def.requireIf
+    validators.requiredIf(def, value, key, errors, data)
+  },
 
   /**
-   * Validator requireIfNot method, used by model
+   * Validator requiredIfNot method, used by model
    * @param {Object} def The property configuration
    * @param {*} value The value being validated
    * @param {string} key The key name of the property
@@ -99,9 +109,9 @@ const validators = {
    * to which any additional error objects will be added
    * @param {Object} data The full initial data object
    */
-  requireIfNot: function(def, value, key, errors, data) {
-    const type = 'requireIfNot'
-    const sub = def.requireIfNot
+  requiredIfNot: function(def, value, key, errors, data) {
+    const type = 'requiredIfNot'
+    const sub = def.requiredIfNot
     if (typeof sub === 'object') {
       const field = Object.keys(sub)[0]
       if (dot.pick(field, data) !== sub[field] && value === undefined) {
@@ -111,6 +121,15 @@ const validators = {
       errors.push({ type, sub, key, value, message: `Value required because '${sub}' is undefined`})
     }
   },
+  /**
+   * Alias for requiredIfNot
+   */
+  requireIfNot: function(def, value, key, errors, data) {
+    console.log('-----\nObey Warning: `requireIfNot` should be `requiredIfNot`\n-----')
+    def.requiredIfNot = def.requireIfNot
+    delete def.requireIfNot
+    validators.requiredIfNot(def, value, key, errors, data)
+  },
 
   /**
    * Validator equalTo method, used by model
diff --git a/src/rules.js b/src/rules.js
index 5f42934..c545f0c 100644
--- a/src/rules.js
+++ b/src/rules.js
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2015 TechnologyAdvice
  */
+/* eslint no-console: 0 */
 import types from './types'
 import modifiers from './modifiers'
 import creators from './creators'
@@ -20,6 +21,8 @@ const allProps = {
   min: { name: 'min', fn: validators.min },
   max: { name: 'max', fn: validators.max },
   type: { name: 'type', fn: types.validate },
+  requiredIf: { name: 'requiredIf', fn: validators.requiredIf },
+  requiredIfNot: { name: 'requiredIfNot', fn: validators.requiredIfNot },
   requireIf: { name: 'requireIf', fn: validators.requireIf },
   requireIfNot: { name: 'requireIfNot', fn: validators.requireIfNot },
   equalTo: { name: 'equalTo', fn: validators.equalTo }
@@ -45,6 +48,8 @@ const rules = {
       allProps.min,
       allProps.max,
       allProps.type,
+      allProps.requiredIf,
+      allProps.requiredIfNot,
       allProps.requireIf,
       allProps.requireIfNot,
       allProps.equalTo
@@ -54,6 +59,8 @@ const rules = {
       allProps.creator,
       allProps.default,
       allProps.modifier,
+      allProps.requiredIf,
+      allProps.requiredIfNot,
       allProps.requireIf,
       allProps.requireIfNot,
       allProps.equalTo
@@ -129,6 +136,12 @@ const rules = {
    * @returns {Array}
    */
   getProps: (def, data) => {
+    //
+    if (def.require) {
+      def.required = def.require
+      delete def.require
+      console.log('-----\nObey Warning: `require` should be `required`\n-----')
+    }
     // Partial and undefined
     if (def.opts.partial && data === undefined) return rules.props.noValPartial
     // Not required, undefined


Test patch:
diff --git a/test/fixtures/core.js b/test/fixtures/core.js
index 01ff042..2e28938 100644
--- a/test/fixtures/core.js
+++ b/test/fixtures/core.js
@@ -7,12 +7,12 @@ export default {
   },
   basicRequired: {
     fname: { type: 'string' },
-    lname: { type: 'string', required: true }
+    lname: { type: 'string', require: true }
   },
   basicExtended: {
     fname: { type: 'string', required: true, min: 2, max: 20 },
     lname: { type: 'string', min: 2, max: 20 },
-    type: { type: 'string', requireIf: 'nested.foo', allowed: [ 'foo', 'bar' ] },
+    type: { type: 'string', requiredIf: 'nested.foo', allowed: [ 'foo', 'bar' ] },
     nested: { type: 'object', values: { type: 'string' } }
   },
   basicCreator: {
diff --git a/test/fixtures/validators.js b/test/fixtures/validators.js
index 6c92d56..d09bdc7 100644
--- a/test/fixtures/validators.js
+++ b/test/fixtures/validators.js
@@ -19,19 +19,19 @@ export default {
   required: {
     name: { type: 'string', required: true }
   },
-  requireIf: {
+  requiredIf: {
     phone: { type: 'phone' },
-    phoneType: { type: 'string', requireIf: 'phone' },
+    phoneType: { type: 'string', requiredIf: 'phone' },
     address: { type: 'object', keys: {
       street: { type: 'string' },
-      city: { type: 'string', requireIf: 'address.street' }
+      city: { type: 'string', requiredIf: 'address.street' }
     }}
   },
-  requireIfNot: {
+  requiredIfNot: {
     address: { type: 'object', keys: {
       street: { type: 'string' },
       state: { type: 'string' },
-      country: { type: 'string', requireIfNot: 'address.state' }
+      country: { type: 'string', requiredIfNot: 'address.state' }
     }}
   }
 }
diff --git a/test/integration/core.spec.js b/test/integration/core.spec.js
index 09d2531..9476d0e 100644
--- a/test/integration/core.spec.js
+++ b/test/integration/core.spec.js
@@ -4,6 +4,10 @@ import modelFixtures from 'test/fixtures/core'
 import ValidationError from 'src/lib/error'
 
 describe('integration:core', () => {
+  let stub
+  afterEach(() => {
+    if (stub) stub.restore()
+  })
   it('builds a model and successfully validates passing object', () => {
     const testModel = obey.model(modelFixtures.basicExtended)
     const testData = {
@@ -55,6 +59,7 @@ describe('integration:core', () => {
       })
   })
   it('builds a model and fails when required field is undefined', () => {
+    stub = sinon.stub(console, 'log')
     const testModel = obey.model(modelFixtures.basicRequired)
     const testData = {
       fname: 'John'
@@ -63,6 +68,7 @@ describe('integration:core', () => {
       .then(() => { throw new Error('Should fail') })
       .catch((err) => {
         expect(err.message).to.equal('lname (undefined): Property \'lname\' is required')
+        expect(stub).calledWith('-----\nObey Warning: `require` should be `required`\n-----')
       })
   })
   it('builds a model and successfully validates when nested object present', () => {
diff --git a/test/integration/validators.spec.js b/test/integration/validators.spec.js
index 873dd60..e1a3cb5 100644
--- a/test/integration/validators.spec.js
+++ b/test/integration/validators.spec.js
@@ -86,20 +86,20 @@ describe('integration:validators', () => {
       })
     })
   })
-  describe('requireIf', () => {
+  describe('requiredIf', () => {
     it('builds a model and fails validation because conditionally required value is undefined', () => {
-      const testModel = obey.model(modelFixtures.requireIf)
+      const testModel = obey.model(modelFixtures.requiredIf)
       const testData = { phone: 5551234567, address: { street: '123 test ave' } }
       return testModel.validate(testData).catch(err => {
         expect(err.collection).to.deep.equal([{
-          type: 'requireIf',
+          type: 'requiredIf',
           sub: 'phone',
           key: 'phoneType',
           value: undefined,
           message: 'Value required because \'phone\' exists'
         },
         {
-          type: 'requireIf',
+          type: 'requiredIf',
           sub: 'address.street',
           key: 'address.city',
           value: undefined,
@@ -108,13 +108,13 @@ describe('integration:validators', () => {
       })
     })
   })
-  describe('requireIfNot', () => {
+  describe('requiredIfNot', () => {
     it('builds a model and fails validation because conditionally required value is undefined', () => {
-      const testModel = obey.model(modelFixtures.requireIfNot)
+      const testModel = obey.model(modelFixtures.requiredIfNot)
       const testData = { address: { street: '123 test ave' } }
       return testModel.validate(testData).catch(err => {
         expect(err.collection).to.deep.equal([{
-          type: 'requireIfNot',
+          type: 'requiredIfNot',
           sub: 'address.state',
           key: 'address.country',
           value: undefined,
diff --git a/test/src/lib/validators.spec.js b/test/src/lib/validators.spec.js
index eaf213f..2a6403b 100644
--- a/test/src/lib/validators.spec.js
+++ b/test/src/lib/validators.spec.js
@@ -141,13 +141,13 @@ describe('validators', () => {
       })
     })
   })
-  describe('requireIf', () => {
+  describe('requiredIf', () => {
     it('creates an error object if conditionally required value is undefined', () => {
       const data = { address: { street: '123 test ave' } }
-      const def = { requireIf: 'address.street' }
-      validators.requireIf(def, undefined, 'address.city', mockErrors, data)
+      const def = { requiredIf: 'address.street' }
+      validators.requiredIf(def, undefined, 'address.city', mockErrors, data)
       expect(mockErrors[0]).to.deep.equal({
-        type: 'requireIf',
+        type: 'requiredIf',
         sub: 'address.street',
         key: 'address.city',
         value: undefined,
@@ -156,10 +156,10 @@ describe('validators', () => {
     })
     it('creates an error object if conditionally required value is undefined when corresponding field HAS the given value', () => {
       const data = { address: { street: '123 test ave', country: 'US' } }
-      const def = { requireIf: { 'address.country': 'US' } }
-      validators.requireIf(def, undefined, 'address.zip', mockErrors, data)
+      const def = { requiredIf: { 'address.country': 'US' } }
+      validators.requiredIf(def, undefined, 'address.zip', mockErrors, data)
       expect(mockErrors[0]).to.deep.equal({
-        type: 'requireIf',
+        type: 'requiredIf',
         sub: { 'address.country': 'US' },
         key: 'address.zip',
         value: undefined,
@@ -167,13 +167,33 @@ describe('validators', () => {
       })
     })
   })
-  describe('requireIfNot', () => {
+  describe('requireIf', () => {
+    let stub
+    after(() => {
+      stub.restore()
+    })
+    it('logs a warning and calls requiredIf method', () => {
+      stub = sinon.stub(console, 'log')
+      const data = { address: { street: '123 test ave' } }
+      const def = { requireIf: 'address.street' }
+      validators.requireIf(def, undefined, 'address.city', mockErrors, data)
+      expect(mockErrors[0]).to.deep.equal({
+        type: 'requiredIf',
+        sub: 'address.street',
+        key: 'address.city',
+        value: undefined,
+        message: 'Value required because \'address.street\' exists'
+      })
+      expect(stub).calledWith('-----\nObey Warning: `requireIf` should be `requiredIf`\n-----')
+    })
+  })
+  describe('requiredIfNot', () => {
     it('creates an error object if conditionally required value is undefined', () => {
       const data = { address: { street: '123 test ave' } }
-      const def = { requireIfNot: 'address.state' }
-      validators.requireIfNot(def, undefined, 'address.country', mockErrors, data)
+      const def = { requiredIfNot: 'address.state' }
+      validators.requiredIfNot(def, undefined, 'address.country', mockErrors, data)
       expect(mockErrors[0]).to.deep.equal({
-        type: 'requireIfNot',
+        type: 'requiredIfNot',
         sub: 'address.state',
         key: 'address.country',
         value: undefined,
@@ -182,10 +202,10 @@ describe('validators', () => {
     })
     it('creates an error object if conditionally required value is undefined when corresponding field does NOT have the given value', () => {
       const data = { testField: 'not what we want' }
-      const def = { requireIfNot: { testField: 'what we want' } }
-      validators.requireIfNot(def, undefined, 'conditionalField', mockErrors, data)
+      const def = { requiredIfNot: { testField: 'what we want' } }
+      validators.requiredIfNot(def, undefined, 'conditionalField', mockErrors, data)
       expect(mockErrors[0]).to.deep.equal({
-        type: 'requireIfNot',
+        type: 'requiredIfNot',
         sub: { testField: 'what we want' },
         key: 'conditionalField',
         value: undefined,
@@ -193,6 +213,26 @@ describe('validators', () => {
       })
     })
   })
+  describe('requireIfNot', () => {
+    let stub
+    after(() => {
+      stub.restore()
+    })
+    it('logs a warning and calls requiredIfNot method', () => {
+      stub = sinon.stub(console, 'log')
+      const data = { address: { street: '123 test ave' } }
+      const def = { requireIfNot: 'address.state' }
+      validators.requireIfNot(def, undefined, 'address.country', mockErrors, data)
+      expect(mockErrors[0]).to.deep.equal({
+        type: 'requiredIfNot',
+        sub: 'address.state',
+        key: 'address.country',
+        value: undefined,
+        message: 'Value required because \'address.state\' is undefined'
+      })
+      expect(stub).calledWith('-----\nObey Warning: `requireIfNot` should be `requiredIfNot`\n-----')
+    })
+  })
   describe('equalTo', () => {
     it('creates an error object when fields are not equal', () => {
       const data = { password: 'Password' }
diff --git a/test/src/rules.spec.js b/test/src/rules.spec.js
index 3a86c50..66b42aa 100644
--- a/test/src/rules.spec.js
+++ b/test/src/rules.spec.js
@@ -30,7 +30,7 @@ describe('rules', () => {
         type: 'object',
         keys: {
           phone: { type: 'string' },
-          phoneType: { type: 'string', requireIf: 'phone' }
+          phoneType: { type: 'string', requiredIf: 'phone' }
         }
       }
       return rules.validate(def, { phone: '123' })
@@ -48,7 +48,7 @@ describe('rules', () => {
           }
         },
           contacts: { type: 'object', keys: {
-            phone: { type: 'string', requireIf: 'person.name' }
+            phone: { type: 'string', requiredIf: 'person.name' }
           }}
         }
       }
@@ -68,7 +68,7 @@ describe('rules', () => {
           labels: { type: 'array', values: {
             type: 'object', keys: {
               label: { type: 'string' },
-              condRequired: { type: 'string', requireIf: 'person.name' }
+              condRequired: { type: 'string', requiredIf: 'person.name' }
             }
           }}
         }