Skip to content

Commit

Permalink
Inline Validator (#569)
Browse files Browse the repository at this point in the history
  • Loading branch information
offirgolan authored Feb 16, 2018
1 parent 9aa92a7 commit a161318
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 148 deletions.
11 changes: 1 addition & 10 deletions addon/validations/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,6 @@ function createValidatorsFor(attribute, model) {
let validatorCache = get(validations, '_validators');
let owner = getOwner(model);
let validators = [];
let validator;

// We must have an owner to be able to lookup our validators
if (isNone(owner)) {
Expand All @@ -763,15 +762,7 @@ function createValidatorsFor(attribute, model) {
validationRules.forEach(v => {
v.attribute = attribute;
v.model = model;

// If validate function exists, that means validator was created with a function so use the base class
if (v._type === 'function') {
validator = BaseValidator.create(owner.ownerInjection(), v);
} else {
validator = lookupValidator(owner, v._type).create(v);
}

validators.push(validator);
validators.push(lookupValidator(owner, v._type).create(v));
});

// Add validators to model instance cache
Expand Down
14 changes: 11 additions & 3 deletions addon/validations/validator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isNone } from '@ember/utils';
import { deprecate } from '@ember/application/deprecations';

/**
* @module Validators
Expand Down Expand Up @@ -210,13 +211,20 @@ export default function(arg1, options) {
};

if (typeof arg1 === 'function') {
props.validate = arg1;
props._type = 'function';
deprecate(
'[ember-cp-validations] `validator` no longer directly accepts ' +
'a function. Please use the inline validator syntax:' +
"\n\nvalidator('inline', { validate() {} )\n\n",
false,
{ id: 'ember-cp-validations.inline-validator', until: '4.2.0' }
);
props.options.validate = arg1;
props._type = 'inline';
} else if (typeof arg1 === 'string') {
props._type = arg1;
} else {
throw new TypeError(
'[ember-cp-validations] Unexpected type for first validator argument. It should either be a string or a function'
'[ember-cp-validations] Unexpected type for first validator argumentIt must be a string.'
);
}

Expand Down
21 changes: 2 additions & 19 deletions addon/validators/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const Base = EmberObject.extend({
/**
* Build options hook. Merges default options into options object.
* This method gets called on init and is the ideal place to normalize your options.
* The [presence validator](https://github.com/offirgolan/ember-cp-validations/blob/master/app/validators/presence.js) is a good example to checkout
* The [presence validator](https://github.com/offirgolan/ember-cp-validations/blob/master/addon/validators/presence.js) is a good example to checkout
* @method buildOptions
* @param {Object} options
* @param {Object} defaultOptions
Expand Down Expand Up @@ -302,7 +302,7 @@ const Base = EmberObject.extend({

Base.reopenClass({
/**
* Generate the needed depenent keys for this validator
* Generate the needed dependent keys for this validator
*
* @method getDependentsFor
* @static
Expand Down Expand Up @@ -466,20 +466,3 @@ export default Base;
* @module Validators
* @extends Base
*/

/**
* A validator can also be declared with an inline function. The function will be then wrapped in the {{#crossLink 'Base'}}Base Validator{{/crossLink}} class and used just like any other pre-defined validator.
*
* ```javascript
* // Example
* validator(function(value, options, model, attribute) {
* return value === options.username ? true : `{description} must be ${options.username}`;
* } , {
* username: 'John' // Any options can be passed here
* })
* ```
*
* @class Inline
* @module Validators
* @extends Base
*/
49 changes: 49 additions & 0 deletions addon/validators/inline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Base from 'ember-cp-validations/validators/base';
import { assign } from '@ember/polyfills';
import { assert } from '@ember/debug';

/**
* Accepts a custom `validate` function.
*
* ## Examples
*
* ```javascript
* validator('inline', {
* username: 'offirgolan',
* validate(value, options, model, attribute) {
* return value === options.username ?
* true :
* `Username must be ${options.username}`;
* }
* });
* ```
*
* @class Inline
* @module Validators
* @extends Base
*/
export default Base.extend({
/**
* Override the validator's `validate` method with the one that was
* passed in via the options.
*
* @method buildOptions
* @param {Object} options
* @param {Object} defaultOptions
* @param {Object} globalOptions
* @return {Object}
*/
buildOptions(options = {}, ...args) {
assert(
`[validator:inline] You must pass in a validate function`,
options && typeof options.validate === 'function'
);

const opts = assign({}, options);

this.validate = opts.validate;
delete opts.validate;

return this._super(opts, ...args);
}
});
1 change: 1 addition & 0 deletions app/validators/inline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'ember-cp-validations/validators/inline';
6 changes: 4 additions & 2 deletions tests/dummy/app/models/signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { validator, buildValidations } from 'ember-cp-validations';

let Validations = buildValidations({
name: validator('presence', true),
acceptTerms: validator(value => {
return value || 'You must accept the terms.';
acceptTerms: validator('inline', {
validate(value) {
return value || 'You must accept the terms.';
}
})
});

Expand Down
60 changes: 24 additions & 36 deletions tests/integration/validations/factory-dependent-keys-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,17 @@ test('nested ds-error validator creates correct dependent keys', function(assert

test('custom dependent keys - simple', function(assert) {
let Validations = buildValidations({
fullName: validator(
function(value, options, model) {
fullName: validator('inline', {
dependentKeys: ['model.firstName', 'model.lastName'],
validate(value, options, model) {
let firstName = model.get('firstName');
let lastName = model.get('lastName');
if (!firstName || !lastName) {
return 'Full name requires first and last name';
}
return true;
},
{
dependentKeys: ['model.firstName', 'model.lastName']
}
)
})
});

let obj = setupObject(this, EmberObject.extend(Validations));
Expand All @@ -127,19 +125,17 @@ test('custom dependent keys - default options', function(assert) {
fullName: {
dependentKeys: ['model.firstName'],
validators: [
validator(
function(value, options, model) {
validator('inline', {
dependentKeys: ['model.lastName'],
validate(value, options, model) {
let firstName = model.get('firstName');
let lastName = model.get('lastName');
if (!firstName || !lastName) {
return 'Full name requires first and last name';
}
return true;
},
{
dependentKeys: ['model.lastName']
}
)
})
]
}
});
Expand All @@ -166,20 +162,18 @@ test('custom dependent keys - global options', function(assert) {
fullName: {
dependentKeys: ['model.firstName'],
validators: [
validator(
function(value, options, model) {
validator('inline', {
dependentKeys: ['model.lastName'],
validate(value, options, model) {
let firstName = model.get('firstName');
let lastName = model.get('lastName');
let middleName = model.get('middleName');
if (!firstName || !lastName || !middleName) {
return 'Full name requires first, middle, and last name';
}
return true;
},
{
dependentKeys: ['model.lastName']
}
)
})
]
}
},
Expand Down Expand Up @@ -211,19 +205,17 @@ test('custom dependent keys - global options', function(assert) {

test('custom dependent keys - nested object', function(assert) {
let Validations = buildValidations({
page: validator(
function(value, options, model) {
page: validator('inline', {
dependentKeys: ['model.currPage', 'model.meta.pages.last'],
validate(value, options, model) {
let currPage = model.get('currPage');
let lastPage = model.get('meta.pages.last');
if (currPage > lastPage) {
return 'Cannot exceed max page';
}
return true;
},
{
dependentKeys: ['model.currPage', 'model.meta.pages.last']
}
)
})
});

let obj = setupObject(this, EmberObject.extend(Validations), {
Expand Down Expand Up @@ -255,18 +247,16 @@ test('custom dependent keys - nested object', function(assert) {

test('custom dependent keys - array', function(assert) {
let Validations = buildValidations({
friends: validator(
function(value, options, model) {
friends: validator('inline', {
dependentKeys: ['model.friends.[]'],
validate(value, options, model) {
let friends = model.get('friends');
if (!friends || friends.length === 0) {
return 'User must have a friend';
}
return true;
},
{
dependentKeys: ['model.friends.[]']
}
)
})
});

let obj = setupObject(this, EmberObject.extend(Validations), {
Expand Down Expand Up @@ -297,8 +287,9 @@ test('custom dependent keys - array', function(assert) {

test('custom dependent keys - array of objects', function(assert) {
let Validations = buildValidations({
friends: validator(
function(value, options, model) {
friends: validator('inline', {
dependentKeys: ['[email protected]'],
validate(value, options, model) {
let friends = model.get('friends');
if (!friends || friends.length === 0) {
return 'User must have a friend';
Expand All @@ -309,11 +300,8 @@ test('custom dependent keys - array of objects', function(assert) {
}
}
return true;
},
{
dependentKeys: ['[email protected]']
}
)
})
});

let obj = setupObject(this, EmberObject.extend(Validations), {
Expand Down
Loading

0 comments on commit a161318

Please sign in to comment.