Skip to content

Commit

Permalink
[FEAT errors] eliminate the call to warn for add/remove/clear
Browse files Browse the repository at this point in the history
  • Loading branch information
runspired authored Dec 5, 2018
1 parent fc68d2a commit 1b06529
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 143 deletions.
181 changes: 97 additions & 84 deletions addon/-private/system/model/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Evented from '@ember/object/evented';
import ArrayProxy from '@ember/array/proxy';
import { set, get, computed } from '@ember/object';
import { makeArray, A } from '@ember/array';
import { warn } from '@ember/debug';

/**
@module ember-data
Expand Down Expand Up @@ -186,27 +185,42 @@ export default ArrayProxy.extend(Evented, {
isEmpty: not('length').readOnly(),

/**
Adds error messages to a given attribute and sends
`becameInvalid` event to the record.
Example:
```javascript
if (!user.get('username') {
user.get('errors').add('username', 'This field is required');
}
```
@method add
@param {String} attribute
@param {(Array|String)} messages
@deprecated
*/
Manually adds errors to the record. This will triger the `becameInvalid` event/ lifecycle method on
the record and transition the record into an `invalid` state.
Example
```javascript
let errors = get(user, 'errors');
// add multiple errors
errors.add('password', [
'Must be at least 12 characters',
'Must contain at least one symbol',
'Cannot contain your name'
]);
errors.errorsFor('password');
// =>
// [
// { attribute: 'password', message: 'Must be at least 12 characters' },
// { attribute: 'password', message: 'Must contain at least one symbol' },
// { attribute: 'password', message: 'Cannot contain your name' },
// ]
// add a single error
errors.add('username', 'This field is required');
errors.errorsFor('password');
// =>
// [
// { attribute: 'username', message: 'This field is required' },
// ]
```
@method add
@param {string} attribute - the property name of an attribute or relationship
@param {string[]|string} messages - an error message or array of error messages for the attribute
*/
add(attribute, messages) {
warn(`Interacting with a record errors object will no longer change the record state.`, false, {
id: 'ds.errors.add',
});

let wasEmpty = get(this, 'isEmpty');

this._add(attribute, messages);
Expand Down Expand Up @@ -257,45 +271,32 @@ export default ArrayProxy.extend(Evented, {
},

/**
Removes all error messages from the given attribute and sends
`becameValid` event to the record if there no more errors left.
Example:
```app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
email: DS.attr('string'),
twoFactorAuth: DS.attr('boolean'),
phone: DS.attr('string')
});
```
```app/routes/user/edit.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
save: function(user) {
if (!user.get('twoFactorAuth')) {
user.get('errors').remove('phone');
}
user.save();
}
}
});
```
@method remove
@param {String} attribute
@deprecated
*/
Manually removes all errors for a given member from the record.
This will transition the record into a `valid` state, and
triggers the `becameValid` event and lifecycle method.
Example:
```javascript
let errors = get('user', errors);
errors.add('phone', ['error-1', 'error-2']);
errors.errorsFor('phone');
// =>
// [
// { attribute: 'phone', message: 'error-1' },
// { attribute: 'phone', message: 'error-2' },
// ]
errors.remove('phone');
errors.errorsFor('phone');
// => undefined
```
@method remove
@param {string} member - the property name of an attribute or relationship
*/
remove(attribute) {
warn(`Interacting with a record errors object will no longer change the record state.`, false, {
id: 'ds.errors.remove',
});

if (get(this, 'isEmpty')) {
return;
}
Expand Down Expand Up @@ -327,32 +328,44 @@ export default ArrayProxy.extend(Evented, {
},

/**
Removes all error messages and sends `becameValid` event
to the record.
Example:
```app/routes/user/edit.js
import Route from '@ember/routing/route';
export default Route.extend({
actions: {
retrySave: function(user) {
user.get('errors').clear();
user.save();
}
}
});
```
@method clear
@deprecated
*/
Manually clears all errors for the record.
This will transition the record into a `valid` state, and
will trigger the `becameValid` event and lifecycle method.
Example:
```javascript
let errors = get('user', errors);
errors.add('username', ['error-a']);
errors.add('phone', ['error-1', 'error-2']);
errors.errorsFor('username');
// =>
// [
// { attribute: 'username', message: 'error-a' },
// ]
errors.errorsFor('phone');
// =>
// [
// { attribute: 'phone', message: 'error-1' },
// { attribute: 'phone', message: 'error-2' },
// ]
errors.clear();
errors.errorsFor('username');
// => undefined
errors.errorsFor('phone');
// => undefined
errors.get('messages')
// => []
```
@method remove
*/
clear() {
warn(`Interacting with a record errors object will no longer change the record state.`, false, {
id: 'ds.errors.clear',
});

if (get(this, 'isEmpty')) {
return;
}
Expand Down
36 changes: 11 additions & 25 deletions tests/integration/records/error-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ import RSVP from 'rsvp';
var env, store, Person;
var attr = DS.attr;

function updateErrors(func, assert) {
assert.expectWarning(function() {
run(func);
}, 'Interacting with a record errors object will no longer change the record state.');
}

module('integration/records/error', {
beforeEach: function() {
Person = DS.Model.extend({
Expand All @@ -36,8 +30,6 @@ module('integration/records/error', {
});

testInDebug('adding errors during root.loaded.created.invalid works', function(assert) {
assert.expect(5);

var person = run(() => {
store.push({
data: {
Expand All @@ -59,11 +51,11 @@ testInDebug('adding errors during root.loaded.created.invalid works', function(a

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.updated.uncommitted');

updateErrors(() => person.get('errors').add('firstName', 'is invalid'), assert);
person.get('errors').add('firstName', 'is invalid');

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.updated.invalid');

updateErrors(() => person.get('errors').add('lastName', 'is invalid'), assert);
person.get('errors').add('lastName', 'is invalid');

assert.deepEqual(person.get('errors').toArray(), [
{ attribute: 'firstName', message: 'is invalid' },
Expand All @@ -72,8 +64,6 @@ testInDebug('adding errors during root.loaded.created.invalid works', function(a
});

testInDebug('adding errors root.loaded.created.invalid works', function(assert) {
assert.expect(5);

let person = store.createRecord('person', {
id: 'wat',
firstName: 'Yehuda',
Expand All @@ -87,11 +77,11 @@ testInDebug('adding errors root.loaded.created.invalid works', function(assert)

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.uncommitted');

updateErrors(() => person.get('errors').add('firstName', 'is invalid'), assert);
person.get('errors').add('firstName', 'is invalid');

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.invalid');

updateErrors(() => person.get('errors').add('lastName', 'is invalid'), assert);
person.get('errors').add('lastName', 'is invalid');

assert.deepEqual(person.get('errors').toArray(), [
{ attribute: 'firstName', message: 'is invalid' },
Expand All @@ -100,8 +90,6 @@ testInDebug('adding errors root.loaded.created.invalid works', function(assert)
});

testInDebug('adding errors root.loaded.created.invalid works add + remove + add', function(assert) {
assert.expect(7);

let person = store.createRecord('person', {
id: 'wat',
firstName: 'Yehuda',
Expand All @@ -113,15 +101,15 @@ testInDebug('adding errors root.loaded.created.invalid works add + remove + add'

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.uncommitted');

updateErrors(() => person.get('errors').add('firstName', 'is invalid'), assert);
person.get('errors').add('firstName', 'is invalid');

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.invalid');

updateErrors(() => person.get('errors').remove('firstName'), assert);
person.get('errors').remove('firstName');

assert.deepEqual(person.get('errors').toArray(), []);

updateErrors(() => person.get('errors').add('firstName', 'is invalid'), assert);
person.get('errors').add('firstName', 'is invalid');

assert.deepEqual(person.get('errors').toArray(), [
{ attribute: 'firstName', message: 'is invalid' },
Expand All @@ -131,8 +119,6 @@ testInDebug('adding errors root.loaded.created.invalid works add + remove + add'
testInDebug('adding errors root.loaded.created.invalid works add + (remove, add)', function(
assert
) {
assert.expect(6);

let person = store.createRecord('person', {
id: 'wat',
firstName: 'Yehuda',
Expand All @@ -144,16 +130,16 @@ testInDebug('adding errors root.loaded.created.invalid works add + (remove, add)

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.uncommitted');

updateErrors(() => {
{
person.get('errors').add('firstName', 'is invalid');
}, assert);
}

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.invalid');

updateErrors(() => {
{
person.get('errors').remove('firstName');
person.get('errors').add('firstName', 'is invalid');
}, assert);
}

assert.equal(person._internalModel.currentState.stateName, 'root.loaded.created.invalid');

Expand Down
Loading

0 comments on commit 1b06529

Please sign in to comment.