docs: function checkSchema(obj, schema, parentKeys, isPartial, baton, callback) {
    var key,
        chain;
  
    async.reduce(Object.keys(schema), {}, function(cleanedObj,
                                                   key,
                                                   reduceCallback) {
      chain = schema[key];
      if (chain.isOptional && obj[key] === null) {
        cleanedObj[key] = null;
        reduceCallback(null, cleanedObj);
      }
      else if (obj.hasOwnProperty(key)) {
        if (chain instanceof Chain) {
          checkChain(obj[key], chain, baton, function(err, cleanedValue) {
            var message;
            if (err) {
              if (err.hasOwnProperty(message)) {
                message = err.message;
              } else {
                message = err;
              }
              reduceCallback({ key: key,
                               parentKeys: parentKeys,
                               message: message });
            } else {
              if (chain.target !== null) {
                key = chain.target;
              }
              cleanedObj[key] = cleanedValue;
              reduceCallback(err, cleanedObj);
            }
          });
        } else {
          // Schema contains a subschema
          parentKeys.push(key);
          checkSchema(obj[key], chain, parentKeys, isPartial, baton,
                      function(err, cleanedValue) {
            if (err) {
              reduceCallback(err);
            } else {
              cleanedObj[key] = cleanedValue;
              reduceCallback(null, cleanedObj);
            }
          });
        }
      } else if ((isPartial && !chain.isUpdateRequired) || chain.isOptional) {
        reduceCallback(null, cleanedObj);
      } else {
        reduceCallback({ key: key,
                         parentKeys: parentKeys,
                         message: 'Missing required key (' + key + ')' });
      }
    },
    callback
    );
  }