Repository: TechnologyAdvice/obey
Pull request #38 (2016-03-22 18:52:57)

Prevent running creators on partials


Patch:
diff --git a/API.md b/API.md
index c9a52e5..4d76db1 100644
--- a/API.md
+++ b/API.md
@@ -261,7 +261,7 @@ Adds new rule to the lib
 
 <a name="rules.getProps"></a>
 ### rules.getProps(def, data) ⇒ <code>Array</code>
-Gets props list according to required and allowNull specifications
+Gets props list according to partial, required, and allowNull specifications
 
 **Kind**: static method of <code>[rules](#rules)</code>  
 
diff --git a/README.md b/README.md
index 7715b69..d98fc1a 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,8 @@ The default for the partial option is `false`, but passing `true` will allow for
 
 The common use-case for validating partials is `PATCH` updates.
 
+_Note: Running a partial validation will prevent running `creator`'s on any properties_
+
 ### Validation Error Handling
 
 Validation errors are collected and thrown after all validation has run. This is as opposed to blocking, or stopping, on the first failure.
diff --git a/src/rules.js b/src/rules.js
index d9bcda7..2afe08e 100644
--- a/src/rules.js
+++ b/src/rules.js
@@ -43,12 +43,14 @@ const rules = {
       allProps.max,
       allProps.type
     ],
-    // When no value/undefined
+    // No value/undefined
     noVal: [
       allProps.creator,
       allProps.default,
       allProps.modifier
-    ]
+    ],
+    // No value, partial
+    noValPartial: []
   },
 
   /**
@@ -109,19 +111,20 @@ const rules = {
   },
 
   /**
-   * Gets props list according to required and allowNull specifications
+   * Gets props list according to partial, required, and allowNull specifications
    * @memberof rules
    * @param {Object} def The rule definition
    * @param {*} data The value being evaluated
    * @returns {Array}
    */
   getProps: (def, data) => {
-    if (!def.required && data === undefined) {
-      return rules.props.noVal
-    }
-    if (def.allowNull && data === null) {
-      return rules.props.noVal
-    }
+    // Partial and undefined
+    if (def.opts.partial && data === undefined) return rules.props.noValPartial
+    // Not required, undefined
+    if (!def.required && data === undefined) return rules.props.noVal
+    // AllowNull and null
+    if (def.allowNull && data === null) return rules.props.noVal
+    // Use default
     return rules.props.default
   }
 }
diff --git a/src/types.js b/src/types.js
index 4a7ea8d..4da6ee9 100644
--- a/src/types.js
+++ b/src/types.js
@@ -58,10 +58,6 @@ const types = {
       errors.push({ type: 'required', sub: 'default', key, value, message: `Property '${key}' is required` })
       return value
     }
-    // Don't run if undefined and partial
-    if (value === undefined && def.opts.partial) {
-      return value
-    }
     // Execute check
     return types.check({ def: parsedDef, key, value, fail, errors })
   },


Test patch:
diff --git a/test/fixtures/core.js b/test/fixtures/core.js
index 6bb0f9a..1dce1f6 100644
--- a/test/fixtures/core.js
+++ b/test/fixtures/core.js
@@ -14,6 +14,10 @@ export default {
     lname: { type: 'string', min: 2, max: 20 },
     type: { type: 'string', allowed: [ 'foo', 'bar' ] }
   },
+  basicCreator: {
+    foo: { type: 'string', creator: 'testCreator' },
+    bar: { type: 'string' }
+  },
   basicNested: {
     name: { type: 'string' },
     someobj: { type: 'object', keys: {
diff --git a/test/integration/core.spec.js b/test/integration/core.spec.js
index e61b5ea..827b145 100644
--- a/test/integration/core.spec.js
+++ b/test/integration/core.spec.js
@@ -147,4 +147,15 @@ describe('integration:core', () => {
         expect(data).to.deep.equal(testData)
       })
   })
+  it('builds a model and passes validation when partial option is set to true, does not run creators', () => {
+    obey.creator('testCreator', () => 'fizz')
+    const testModel = obey.model(modelFixtures.basicCreator)
+    const testData = {
+      bar: 'buzz'
+    }
+    return testModel.validate(testData, { partial: true })
+      .then(data => {
+        expect(data).to.deep.equal(testData)
+      })
+  })
 })