1602 / 1602/jugglingdb

Streamline validations

オープン
#362 コメント 10 件 リアクション 0 件 担当者 0 名 GitHub で見る
in progress
主要言語
JavaScript
スター
2k
フォーク
238
PR マージ指標
30日以内にマージされた PR はありません

説明

### Rationale

The first thing I noticed when reading the documentation (I'm actually evaluating this project as a suitable ORM for our own project) was the hard-coded base validators and it's lack of flexibility. Reading the issues, I see many tickets on the subject.

Having a non-negligible Zend Framework background, the way validators are handled has inspired many other projects, as it allows effortless flexibility and extensibility. An other such project is [Backbone.Validation](https://github.com/thedersen/backbone.validation).
### Suggestion

So, instead of

```
// Setup validations
User.validatesPresenceOf('name', 'email')
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
User.validatesInclusionOf('gender', {in: ['male', 'female']});
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
User.validatesNumericalityOf('age', {int: true});
User.validatesUniquenessOf('email', {message: 'email is not unique'});
```

I would have something like

```
User.validation({
name: {
required: true // true or string, ex: "Name is not specified!"
},
email: {
required: "Email is not specified!" // or true
, unique: "Email is not unique!"
},
password: {
minLength: { min: 5, message: "Password must contain between 5 and 16 caracters" }
, maxLength: { max: 16, message: "Password must contain between 5 and 16 caracters" }
},
gender: {
in: { values: ["male", "femaile"], ignoreCase: true /*, message: .... */ }
},
domaine: {
notIn: ['www', 'billing', 'admin']
},
age: {
integer: true
, min: { value: 18, message: "You must be of legal age!" }
}
});
```

Then, adding new validators could be done like

```
var Schema = require('jugglingdb').Schema;

// signature : add(name, callback, async)
// - name String the validator's name
// - callback Function the validation function :
// - model Object the model instance
// - fieldName String the model field to validate
// - options Object the options configuration
// - callback Function the async callback (optional)
// returns true for success
// returns false or a string (error message) on failure
// - async Boolean if the validation function is async or not (optional) (default false)
Schema.Validation.add("foo", function (model, fieldName, options) {
/* ... */
return true;
});

// async validation must pass a fourth parameter 'cb'
Schema.Validation.add("bar", function (model, fieldName, options, cb) {
setTimeout(function() {
cb(true);
}, 1000);
}, true);

// to use generators, simply provide the async flag and omit the fourth parameter 'cb'
Schema.Validation.add("buz", function * (model, fieldName, options) {
var asyncResult = yield someAsyncCall(model[fieldName], options);
/* ... */
return true;
}, true);

// using each custom validators (the third parameter 'options' will be "true")
User.validation({
name: {
foo: true,
bar: true,
buz: true
}
};
```
### Internationalisation

All error messages should support a `sprintf`-like syntax. With Node, this is supported via the [`util.format`](http://nodejs.org/docs/latest/api/util.html#util_util_format_format) function. The default fallback message on failed validation (if the validation function returns `false`) could be `"'%s' validation failed!"`, which will be formatted with the validated `fieldName`.

To enable translation of these messages, simply provide a function to

```
Schema.Validation.translator(function(message) {
return message; // translated
});
```

Obviously, since all (failed) validation messages are formatted inside the validator functions, before being returned, the translations should also be performed before formatting the message. Therefore, and since we already have a reference to `Schema.Validation`, I would propose

```
var format = require('util').format;
Schema.Validation.add("foo", function (model, fieldName, options) {
return format(Schema.Validation.translate("Foo failed for '%s'!"), fieldName);
});
```
### Integration and Backward compatibility

To support existing projects, all current implementations could remain unchanged and simply proxying to the new validation system, to be deprecated at the next minor release (version 0.3). Thus, the built-in current validators could still be shipped with the ORM, along with new ones provided by the community.
#### `validatesPresenceOf` becomes `required`
##### Options

```
{
message: (defaults to "Field %s is required.")
}
```

The model field must be specified, with any given value.
#### `validatesLengthOf` becomes `minLength` and `maxLength`
##### Options

```
{
min: (defaults to 3)
, max: (defaults to 255)
, message: (defaults to "Field '%s' must contain more|less than %d character(s)")
}
```

For strings, check it's `length` property. Ignored for any other type.
#### `validatesInclusionOf` becomes `in`
##### Options

```
{
values:
, strictCompare: (defaults true)
, ignoreCase: (defaults false)
, message: (defaults to "Invalid field value '%s'")
}
```

The `values` array items' type should be compatible with the model's field type.
#### `validatesExclusionOf` becomes `notIn`
##### Options

```
{
values:
, strictCompare:
, ignoreCase: (defaults false)
, message: (defaults to "Invalid field value '%s'")
}
```

The `values` array items' type should be compatible with the model's field type.
#### `validatesNumericalityOf` becomes `number`, `integer`, `decimal`, or `finite`
##### Options

```
{
message: (defaults to "Invalid numeric value '%s'")
}
```

For `number`, the value must be _any_ numeric value.
For `integer`, the value must be an integer value.
For `decimal`, the value must _not_ be an integer value.
For `finite`, the value must be _finite_.
#### `validatesUniquenessOf` becomes `unique`
##### Options

```
{
message: (defaults to "%s must be unique.")
}
```

This validator checks in all the set for another model with the same field value. This validator is asynchronous.
### Other proposed validations

From the [sails.js](http://sailsjs.org/#!documentation/models) framework.
- empty
- notEmpty
- undefined
- string
- alpha
- alphanumeric
- email
- url
- urlish
- ip
- ipv4
- ipv6
- creditcard
- uuid
- uuidv3
- uuidv4
- falsey
- truthy
- null
- notNull
- boolean
- array
- date
- hexadecimal
- hexColor
- lowercase
- uppercase
- after
- before
- is
- regex
- not
- notRegex
- equals
- contains
- notContains
- len
- max
- min

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

この issue は、jugglingdb のバリデーションシステムの大規模な見直しを提案しています。まず、コードベース内の既存のバリデーションメソッド(おそらく lib/validations.js などのファイル)を調べることから始めてください。validatesPresenceOf のような現在のバリデータがどのように実装されているかを理解します。目標は、宣言型フォーマット、カスタムバリデータ、国際化をサポートする新しいバリデーション API を設計することです。これには ORM のアーキテクチャに精通している必要があり、複数のファイルにわたる変更が含まれる可能性があります。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, nodejs
領域
api, backend, databases
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。