Skip to content

Commit 98fdd7c

Browse files
committed
Merge pull request freewil#4 from freewil/async
add custom asyncronous validator support
2 parents 053290c + aaddaf8 commit 98fdd7c

File tree

8 files changed

+232
-69
lines changed

8 files changed

+232
-69
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
language: node_js
22
node_js:
3-
- 0.4
4-
- 0.6
5-
- 0.8
3+
- "0.8"
4+
- "0.10"

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ Use "%s" in the message to have the field name or label printed in the message:
320320
321321
field("sport", "favorite sport").custom(function(value, source) {
322322
if (!source.country) {
323-
throw new error('unable to validate %s');
323+
throw new Error('unable to validate %s');
324324
}
325325
326326
switch (source.country) {
@@ -338,6 +338,15 @@ Use "%s" in the message to have the field name or label printed in the message:
338338
}
339339
340340
});
341+
342+
Asynchronous custom validator (3 argument function signature)
343+
344+
form.field('username').custom(function(value, source, callback) {
345+
username.check(value, function(err) {
346+
if (err) return callback(new Error('Invalid %s'));
347+
callback(null);
348+
});
349+
});
341350

342351

343352
### http.ServerRequest.prototype.form

lib/field.js

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var validator = require("validator")
44
, externalFilter = new validator.Filter()
55
, externalValidator = new validator.Validator()
66
, object = require("object-additions").object
7+
, async = require("async")
78
, utils = require("./utils");
89

910
function Field(property, label) {
@@ -24,7 +25,7 @@ function Field(property, label) {
2425
return this;
2526
};
2627

27-
this.run = function (source, form, options) {
28+
this.run = function (source, form, options, cb) {
2829
var self = this
2930
, errors = []
3031
, value = utils.getProp(property, form) || utils.getProp(property, source);
@@ -38,36 +39,62 @@ function Field(property, label) {
3839
});
3940
}
4041

41-
function runStack(foo) {
42+
function runStack(foo, cb) {
4243

43-
stack.forEach(function (proc) {
44-
var result = proc(foo, source); // Pass source for "equals" proc.
45-
if (result.valid) return;
44+
async.eachSeries(stack, function(proc, cb) {
45+
46+
if (proc.length == 3) {
47+
// run the async validator/filter
48+
return proc(foo, source, function(err, result) {
49+
if (err) {
50+
errors.push(err.message.replace("%s", fieldLabel));
51+
return cb(null);
52+
}
53+
54+
// filters return values
55+
if (result != null) {
56+
foo = result
57+
}
58+
59+
cb(null);
60+
61+
});
62+
}
63+
64+
// run the sync validator/filter
65+
var result = proc(foo, source);
66+
if (result.valid) return cb(null);
4667
if (result.error) {
4768
// If this field is not required and it doesn't have a value, ignore error.
48-
if (!utils.hasValue(value) && !self.__required) return;
69+
if (!utils.hasValue(value) && !self.__required) return cb(null);
4970

50-
return errors.push(result.error.replace("%s", fieldLabel));
71+
errors.push(result.error.replace("%s", fieldLabel));
72+
return cb(null);
5173
}
5274
foo = result;
75+
cb(null);
76+
77+
}, function(err) {
78+
cb(null, foo);
5379
});
54-
55-
return foo;
5680
}
5781

5882
if (isArray) {
5983
if (!utils.hasValue(value)) value = [];
6084
if (!Array.isArray(value)) value = [value];
61-
value = value.map(runStack);
85+
async.mapSeries(value, runStack, function(err, value) {
86+
utils.setProp(property, form, value);
87+
cb(null, errors);
88+
});
89+
6290

6391
} else {
6492
if (Array.isArray(value)) value = value[0];
65-
value = runStack(value);
93+
runStack(value, function(err, value) {
94+
utils.setProp(property, form, value);
95+
cb(null, errors);
96+
});
6697
}
67-
68-
utils.setProp(property, form, value);
69-
70-
if (errors.length) return errors;
7198
};
7299
}
73100

@@ -92,7 +119,25 @@ Field.prototype.arrLength = function (from, to) {
92119
// HYBRID METHODS
93120

94121
Field.prototype.custom = function(func, message) {
122+
123+
// custom function is async
124+
if (func.length == 3) {
125+
return this.add(function(value, source, cb) {
126+
func(value, source, function(err, result) {
127+
if (err) return cb(new Error(message || err.message || "%s is invalid"));
128+
129+
// functions that return values are filters
130+
if (result != null) return cb(null, result);
131+
132+
// value passed validator
133+
cb(null, null);
134+
});
135+
});
136+
}
137+
138+
// custom function is sync
95139
return this.add(function (value, source) {
140+
96141
try {
97142
var result = func(value, source);
98143
} catch (e) {
@@ -101,7 +146,9 @@ Field.prototype.custom = function(func, message) {
101146
// Functions that return values are filters.
102147
if (result != null) return result;
103148

149+
// value passed validator
104150
return { valid: true };
151+
105152
});
106153
};
107154

lib/form.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
* MIT Licensed
55
*/
66

7-
var utils = require("./utils")
7+
var async = require("async")
8+
, utils = require("./utils")
89
, Field = require("./field");
910

1011
function form() {
@@ -82,25 +83,32 @@ function form() {
8283
}
8384
});
8485

85-
routines.forEach(function (routine) {
86-
var result = routine.run(mergedSource, req.form, options);
87-
88-
if (!Array.isArray(result) || !result.length) return;
86+
//routines.forEach(function (routine) {
87+
async.each(routines, function(routine, cb) {
88+
routine.run(mergedSource, req.form, options, function(err, result) {
89+
90+
// return early if no errors
91+
if (!Array.isArray(result) || !result.length) return cb(null);
8992

90-
var errors = req.form.errors = req.form.errors || []
91-
, name = routine.name;
93+
var errors = req.form.errors = req.form.errors || []
94+
, name = routine.name;
9295

93-
map[name] = map[name] || [];
96+
map[name] = map[name] || [];
9497

95-
result.forEach(function (error) {
96-
errors.push(error);
97-
map[name].push(error);
98+
result.forEach(function (error) {
99+
errors.push(error);
100+
map[name].push(error);
101+
});
102+
103+
cb(null);
104+
98105
});
106+
}, function(err) {
107+
108+
if (options.flashErrors) req.form.flashErrors();
109+
if (next) next();
110+
99111
});
100-
101-
if (options.flashErrors) req.form.flashErrors();
102-
103-
if (next) next();
104112
}
105113
}
106114

package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,26 @@
2121
],
2222
"dependencies": {
2323
"validator": "0.4.x",
24-
"object-additions": ">= 0.5.0"
24+
"object-additions": ">= 0.5.0",
25+
"async": "~0.2.9"
2526
},
2627
"peerDependencies": {
2728
"express": "3.x"
2829
},
2930
"devDependencies": {
30-
"expresso": "~0.9.2",
31-
"express": "3.x"
31+
"mocha": "~1.13.0",
32+
"express": "3.x",
33+
"request": "~2.27.0"
3234
},
3335
"main": "index",
3436
"bugs": {
3537
"url": "http://github.com/freewil/express-form/issues"
3638
},
3739
"scripts": {
38-
"test": "expresso"
40+
"test": "mocha"
3941
},
4042
"engines": {
41-
"node": ">=0.4.0"
43+
"node": ">=0.8.0"
4244
},
4345
"licenses": [
4446
{

test/express.test.js

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var assert = require("assert"),
44
validate = form.validate,
55
express = require("express"),
66
http = require("http"),
7+
request = require("request"),
78
app = express();
89

910
http.createServer(app).listen(3000);
@@ -16,13 +17,12 @@ app.close = function() {
1617
process.exit(0);
1718
};
1819

19-
2020
app.configure(function() {
2121
app.use(express.bodyParser());
2222
});
2323

2424
module.exports = {
25-
'express : middleware : valid-form': function() {
25+
'express : middleware : valid-form': function(done) {
2626
app.post(
2727
'/user',
2828
form(
@@ -38,23 +38,24 @@ module.exports = {
3838
assert.strictEqual(req.form.errors.length, 0);
3939
res.send(JSON.stringify(req.form));
4040
}
41-
);
42-
43-
assert.response(app,
44-
{
45-
url: '/user',
46-
method: 'POST',
47-
body: JSON.stringify({
48-
username: " dandean \n\n\t",
49-
password: " 12345 "
50-
}),
51-
headers: { 'Content-Type': 'application/json' }
52-
},
53-
{ status: 200 }
5441
);
42+
43+
request.post({
44+
url: 'http://localhost:3000/user',
45+
method: 'POST',
46+
body: JSON.stringify({
47+
username: " dandean \n\n\t",
48+
password: " 12345 "
49+
}),
50+
headers: { 'Content-Type': 'application/json' }
51+
}, function(err, res, body) {
52+
assert.ifError(err);
53+
assert.strictEqual(res.statusCode, 200);
54+
done();
55+
});
5556
},
5657

57-
'express : middleware : merged-data': function() {
58+
'express : middleware : merged-data': function(done) {
5859
app.post(
5960
'/user/:id',
6061
form(
@@ -75,21 +76,22 @@ module.exports = {
7576

7677
res.send(JSON.stringify(req.form));
7778
}
78-
);
79-
80-
assert.response(app,
81-
{
82-
url: '/user/5?stuff=things&id=overridden',
83-
method: 'POST',
84-
body: JSON.stringify({
85-
id: "overridden by url param",
86-
stuff: "overridden by query param",
87-
rad: "cool"
88-
}),
89-
headers: { 'Content-Type': 'application/json' }
90-
},
91-
{ status: 200 }
9279
);
80+
81+
request({
82+
url: 'http://localhost:3000/user/5?stuff=things&id=overridden',
83+
method: 'POST',
84+
body: JSON.stringify({
85+
id: "overridden by url param",
86+
stuff: "overridden by query param",
87+
rad: "cool"
88+
}),
89+
headers: { 'Content-Type': 'application/json' }
90+
}, function(err, res, body) {
91+
assert.ifError(err);
92+
assert.strictEqual(res.statusCode, 200);
93+
done();
94+
});
9395
}
9496

9597

test/mocha.opts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--ui exports

0 commit comments

Comments
 (0)