diff --git a/packages/ember-runtime/lib/mixins/array.js b/packages/ember-runtime/lib/mixins/array.js index 9f6c6bac60b..d9b8d21adec 100644 --- a/packages/ember-runtime/lib/mixins/array.js +++ b/packages/ember-runtime/lib/mixins/array.js @@ -270,19 +270,6 @@ const ArrayMixin = Mixin.create(Enumerable, { return indexes.map(idx => objectAt(this, idx)); }, - /** - @method nextObject - @param {Number} index the current index of the iteration - @param {Object} previousObject the value returned by the last call to - `nextObject`. - @param {Object} context a context object you can use to maintain state. - @return {Object} the next object in the iteration or undefined - @private - */ - nextObject(idx) { - return objectAt(this, idx); - }, - /** This is the handler for the special array content property. If you get this property, it will return this. If you set this property to a new diff --git a/packages/ember-runtime/lib/system/array_proxy.js b/packages/ember-runtime/lib/system/array_proxy.js index cbd23b9baf3..e457ed4f4cc 100644 --- a/packages/ember-runtime/lib/system/array_proxy.js +++ b/packages/ember-runtime/lib/system/array_proxy.js @@ -1,10 +1,12 @@ +/** +@module @ember/array +*/ + import { get, computed, _beforeObserver, observer, - beginPropertyChanges, - endPropertyChanges, alias } from 'ember-metal'; import { @@ -17,15 +19,7 @@ import { removeArrayObserver, objectAt } from '../mixins/array'; -import { assert, Error as EmberError } from 'ember-debug'; - -/** -@module @ember/array -*/ - -const OUT_OF_RANGE_EXCEPTION = 'Index out of range'; -const EMPTY = []; - +import { assert } from 'ember-debug'; /** An ArrayProxy wraps any other object that implements `Array` and/or @@ -182,110 +176,9 @@ export default EmberObject.extend(MutableArray, { // No dependencies since Enumerable notifies length of change }), - _replace(idx, amt, objects) { - let content = get(this, 'content'); - assert(`The content property of ${this.constructor} should be set before modifying it`, content); - if (content) { - this.replaceContent(idx, amt, objects); - } - - return this; - }, - - replace() { - if (get(this, 'arrangedContent') === get(this, 'content')) { - this._replace(...arguments); - } else { - throw new EmberError('Using replace on an arranged ArrayProxy is not allowed.'); - } - }, - - _insertAt(idx, object) { - if (idx > get(this, 'content.length')) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } - - this._replace(idx, 0, [object]); - return this; - }, - - insertAt(idx, object) { - if (get(this, 'arrangedContent') === get(this, 'content')) { - return this._insertAt(idx, object); - } else { - throw new EmberError('Using insertAt on an arranged ArrayProxy is not allowed.'); - } - }, - - removeAt(start, len) { - if ('number' === typeof start) { - let content = get(this, 'content'); - let arrangedContent = get(this, 'arrangedContent'); - let indices = []; - - if ((start < 0) || (start >= get(this, 'length'))) { - throw new EmberError(OUT_OF_RANGE_EXCEPTION); - } - - if (len === undefined) { - len = 1; - } - - // Get a list of indices in original content to remove - for (let i = start; i < start + len; i++) { - // Use arrangedContent here so we avoid confusion with objects transformed by objectAtContent - indices.push(content.indexOf(objectAt(arrangedContent, i))); - } - - // Replace in reverse order since indices will change - indices.sort((a, b) => b - a); - - beginPropertyChanges(); - for (let i = 0; i < indices.length; i++) { - this._replace(indices[i], 1, EMPTY); - } - endPropertyChanges(); - } - - return this; - }, - - pushObject(obj) { - this._insertAt(get(this, 'content.length'), obj); - return obj; - }, - - pushObjects(objects) { - if (!isArray(objects)) { - throw new TypeError('Must pass Enumerable to MutableArray#pushObjects'); - } - this._replace(get(this, 'length'), 0, objects); - return this; - }, - - setObjects(objects) { - if (objects.length === 0) { - return this.clear(); - } - - let len = get(this, 'length'); - this._replace(0, len, objects); - return this; - }, - - unshiftObject(obj) { - this._insertAt(0, obj); - return obj; - }, - - unshiftObjects(objects) { - this._replace(0, 0, objects); - return this; - }, - - slice() { - let arr = this.toArray(); - return arr.slice(...arguments); + replace(idx, amt, objects) { + assert('Mutating an arranged ArrayProxy is not allowed', get(this, 'arrangedContent') === get(this, 'content') ); + this.replaceContent(idx, amt, objects); }, arrangedContentArrayWillChange(item, idx, removedCnt, addedCnt) { diff --git a/packages/ember-runtime/tests/mixins/array_test.js b/packages/ember-runtime/tests/mixins/array_test.js index 88a9215b1f4..2cfd0fd21af 100644 --- a/packages/ember-runtime/tests/mixins/array_test.js +++ b/packages/ember-runtime/tests/mixins/array_test.js @@ -104,7 +104,6 @@ QUnit.test('slice supports negative index arguments', function(assert) { // const DummyArray = EmberObject.extend(EmberArray, { - nextObject() {}, length: 0, objectAt(idx) { return 'ITEM-' + idx; } }); diff --git a/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js b/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js index 9803e7b699e..577e2239e34 100644 --- a/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js +++ b/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js @@ -27,24 +27,6 @@ QUnit.module('ArrayProxy - arrangedContent', { } }); -QUnit.test('addObject - adds to end of \'content\' if not present', function(assert) { - run(() => array.addObject(3)); - - assert.deepEqual(array.get('content'), [1, 2, 4, 5, 3], 'adds to end of content'); - assert.deepEqual(array.get('arrangedContent'), [5, 4, 3, 2, 1], 'arrangedContent stays sorted'); - - run(() => array.addObject(1)); - - assert.deepEqual(array.get('content'), [1, 2, 4, 5, 3], 'does not add existing number to content'); -}); - -QUnit.test('addObjects - adds to end of \'content\' if not present', function(assert) { - run(() => array.addObjects([1, 3, 6])); - - assert.deepEqual(array.get('content'), [1, 2, 4, 5, 3, 6], 'adds to end of content'); - assert.deepEqual(array.get('arrangedContent'), [6, 5, 4, 3, 2, 1], 'arrangedContent stays sorted'); -}); - QUnit.test('compact - returns arrangedContent without nulls and undefined', function(assert) { run(() => array.set('content', emberA([1, 3, null, 2, undefined]))); @@ -55,20 +37,11 @@ QUnit.test('indexOf - returns index of object in arrangedContent', function(asse assert.equal(array.indexOf(4), 1, 'returns arranged index'); }); -QUnit.test('insertAt - raises, indeterminate behavior', function(assert) { - assert.throws(() => run(() => array.insertAt(2, 3))); -}); - QUnit.test('lastIndexOf - returns last index of object in arrangedContent', function(assert) { - run(() => array.pushObject(4)); - + array.get('content').pushObject(4); assert.equal(array.lastIndexOf(4), 2, 'returns last arranged index'); }); -QUnit.test('nextObject - returns object at index in arrangedContent', function(assert) { - assert.equal(array.nextObject(1), 4, 'returns object at index'); -}); - QUnit.test('objectAt - returns object at index in arrangedContent', function(assert) { assert.equal(objectAt(array, 1), 4, 'returns object at index'); }); @@ -82,40 +55,10 @@ QUnit.test('objectsAt - returns objects at indices in arrangedContent', function assert.deepEqual(array.objectsAt([0, 2, 4]), [5, 2, undefined], 'returns objects at indices'); }); -QUnit.test('popObject - removes last object in arrangedContent', function(assert) { - let popped; - run(() => popped = array.popObject()); - assert.equal(popped, 1, 'returns last object'); - assert.deepEqual(array.get('content'), [2, 4, 5], 'removes from content'); -}); - -QUnit.test('pushObject - adds to end of content even if it already exists', function(assert) { - run(() => array.pushObject(1)); - assert.deepEqual(array.get('content'), [1, 2, 4, 5, 1], 'adds to end of content'); -}); - -QUnit.test('pushObjects - adds multiple to end of content even if it already exists', function(assert) { - run(() => array.pushObjects([1, 2, 4])); - assert.deepEqual(array.get('content'), [1, 2, 4, 5, 1, 2, 4], 'adds to end of content'); -}); - -QUnit.test('removeAt - removes from index in arrangedContent', function(assert) { - run(() => array.removeAt(1, 2)); - assert.deepEqual(array.get('content'), [1, 5]); -}); - -QUnit.test('removeObject - removes object from content', function(assert) { - run(() => array.removeObject(2)); - assert.deepEqual(array.get('content'), [1, 4, 5]); -}); - -QUnit.test('removeObjects - removes objects from content', function(assert) { - run(() => array.removeObjects([2, 4, 6])); - assert.deepEqual(array.get('content'), [1, 5]); -}); - -QUnit.test('replace - raises, indeterminate behavior', function(assert) { - assert.throws(() => run(() => array.replace(1, 2, [3]))); +QUnit.test('replace - mutating an arranged ArrayProxy is not allowed', function() { + expectAssertion(() => { + array.replace(0, 0, [3]); + }, /Mutating an arranged ArrayProxy is not allowed/); }); QUnit.test('replaceContent - does a standard array replace on content', function(assert) { @@ -123,22 +66,6 @@ QUnit.test('replaceContent - does a standard array replace on content', function assert.deepEqual(array.get('content'), [1, 3, 5]); }); -QUnit.test('reverseObjects - raises, use Sortable#sortAscending', function(assert) { - assert.throws(() => run(() => array.reverseObjects())); -}); - -QUnit.test('setObjects - replaces entire content', function(assert) { - run(() => array.setObjects([6, 7, 8])); - assert.deepEqual(array.get('content'), [6, 7, 8], 'replaces content'); -}); - -QUnit.test('shiftObject - removes from start of arrangedContent', function(assert) { - let shifted = run(() => array.shiftObject()); - - assert.equal(shifted, 5, 'returns first object'); - assert.deepEqual(array.get('content'), [1, 2, 4], 'removes object from content'); -}); - QUnit.test('slice - returns a slice of the arrangedContent', function(assert) { assert.deepEqual(array.slice(1, 3), [4, 2], 'returns sliced arrangedContent'); }); @@ -147,16 +74,6 @@ QUnit.test('toArray - returns copy of arrangedContent', function(assert) { assert.deepEqual(array.toArray(), [5, 4, 2, 1]); }); -QUnit.test('unshiftObject - adds to start of content', function(assert) { - run(() => array.unshiftObject(6)); - assert.deepEqual(array.get('content'), [6, 1, 2, 4, 5], 'adds to start of content'); -}); - -QUnit.test('unshiftObjects - adds to start of content', function(assert) { - run(function() { array.unshiftObjects([6, 7]); }); - assert.deepEqual(array.get('content'), [6, 7, 1, 2, 4, 5], 'adds to start of content'); -}); - QUnit.test('without - returns arrangedContent without object', function(assert) { assert.deepEqual(array.without(2), [5, 4, 1], 'returns arranged without object'); }); @@ -234,14 +151,10 @@ QUnit.test('indexOf - returns index of object in arrangedContent', function(asse }); QUnit.test('lastIndexOf - returns last index of object in arrangedContent', function(assert) { - run(function() { array.pushObject(4); }); + array.get('content').pushObject(4); assert.equal(array.lastIndexOf('4'), 2, 'returns last arranged index'); }); -QUnit.test('nextObject - returns object at index in arrangedContent', function(assert) { - assert.equal(array.nextObject(1), '4', 'returns object at index'); -}); - QUnit.test('objectAt - returns object at index in arrangedContent', function(assert) { assert.equal(objectAt(array, 1), '4', 'returns object at index'); }); @@ -255,30 +168,6 @@ QUnit.test('objectsAt - returns objects at indices in arrangedContent', function assert.deepEqual(array.objectsAt([0, 2, 4]), ['5', '2', undefined], 'returns objects at indices'); }); -QUnit.test('popObject - removes last object in arrangedContent', function(assert) { - let popped; - run(function() { popped = array.popObject(); }); - assert.equal(popped, '1', 'returns last object'); - assert.deepEqual(array.get('content'), [2, 4, 5], 'removes from content'); -}); - -QUnit.test('removeObject - removes object from content', function(assert) { - run(function() { array.removeObject('2'); }); - assert.deepEqual(array.get('content'), [1, 4, 5]); -}); - -QUnit.test('removeObjects - removes objects from content', function(assert) { - run(function() { array.removeObjects(['2', '4', '6']); }); - assert.deepEqual(array.get('content'), [1, 5]); -}); - -QUnit.test('shiftObject - removes from start of arrangedContent', function(assert) { - let shifted; - run(function() { shifted = array.shiftObject(); }); - assert.equal(shifted, '5', 'returns first object'); - assert.deepEqual(array.get('content'), [1, 2, 4], 'removes object from content'); -}); - QUnit.test('slice - returns a slice of the arrangedContent', function(assert) { assert.deepEqual(array.slice(1, 3), ['4', '2'], 'returns sliced arrangedContent'); }); @@ -299,6 +188,48 @@ QUnit.test('firstObject - returns first arranged object', function(assert) { assert.equal(array.get('firstObject'), '5', 'returns first arranged object'); }); +QUnit.module('ArrayProxy - with transforms', { + beforeEach() { + run(function() { + array = ArrayProxy.extend({ + objectAtContent(idx) { + let obj = objectAt(this.get('arrangedContent'), idx); + return obj && obj.toString(); + } + }).create({ + content: emberA([1, 2, 4, 5]) + }); + }); + }, + afterEach() { + run(function() { + array.destroy(); + }); + } +}); + +QUnit.test('popObject - removes last object in arrangedContent', function(assert) { + let popped = array.popObject(); + assert.equal(popped, '5', 'returns last object'); + assert.deepEqual(array.toArray(), ['1', '2', '4'], 'removes from content'); +}); + +QUnit.test('removeObject - removes object from content', function(assert) { + array.removeObject('2'); + assert.deepEqual(array.toArray(), ['1', '4', '5']); +}); + +QUnit.test('removeObjects - removes objects from content', function(assert) { + array.removeObjects(['2', '4', '6']); + assert.deepEqual(array.toArray(), ['1', '5']); +}); + +QUnit.test('shiftObject - removes from start of arrangedContent', function(assert) { + let shifted = array.shiftObject(); + assert.equal(shifted, '1', 'returns first object'); + assert.deepEqual(array.toArray(), ['2', '4', '5'], 'removes object from content'); +}); + QUnit.test('arrangedContentArray{Will,Did}Change are called when the arranged content changes', function(assert) { // The behavior covered by this test may change in the future if we decide // that built-in array methods are not overridable.