Skip to content

Instantly share code, notes, and snippets.

@sukima
Last active September 20, 2018 22:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sukima/16b77e0c1d57dbbc2b464901106ae8cb to your computer and use it in GitHub Desktop.
Save sukima/16b77e0c1d57dbbc2b464901106ae8cb to your computer and use it in GitHub Desktop.
Use Ember.Array/ArrayProxy/etc. in for...of and yield*
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import { A } from '@ember/array';
import ArrayProxy from '@ember/array/proxy';
import EmberObject from '@ember/object';
import EnumIterator from 'ember-cli-vpp/-enum-iterator';
const TestSubject = EmberObject.extend({
_testData: Object.freeze([1, 2, 3]),
length: 3,
objectAt(index) {
return this._testData[index];
}
});
module('Unit | enum iterator', function(hooks) {
setupTest(hooks);
test('can iterate over an Ember.Array like objects', function(assert) {
let subject = new EnumIterator(TestSubject.create());
assert.expect(3);
for (let result of subject) {
assert.ok(result > 0);
}
});
test('can iterate over an Ember.NativeArray', function(assert) {
let subject = new EnumIterator(A([1, 2, 3]));
assert.expect(3);
for (let result of subject) {
assert.ok(result > 0);
}
});
test('can iterate over an Ember.ArrayProxy', function(assert) {
let subject = new EnumIterator(ArrayProxy.create({ content: [1, 2, 3] }));
assert.expect(3);
for (let result of subject) {
assert.ok(result > 0);
}
});
test('is a no-op (empty list) when base object is not present', function(assert) {
let subject = new EnumIterator();
assert.expect(0);
for (let result of subject) {
assert.ok(result > 0);
}
});
});
/**
* @module Utilities
*/
import { assert } from '@ember/debug';
import { get } from '@ember/object';
function* objectAtIterator(base) {
assert('base object must be an Ember.Array', base.objectAt);
let len = get(base, 'length');
for (let index = 0; index < len; index++) {
yield base.objectAt(index);
}
}
class NullObjectIterator {
next() {
return { done: true };
}
}
/**
* A wrapper around an Ember.Enumerable which enables its use in a for...of
* loop or yield* situations.
*
* Since base ember objects and arrarys do not implement the ES6
* [Symbol.iterator] convention they cannot be used in cases like a for...of
* loop or to be proxied in a yield*
*
* @class EnumIterator
*/
export default class EnumIterator {
/**
* Wrap a base ember object that implements the Ember.Enumerable mixin. This
* includes ember's implementation of Array and ArrayProxy.
* @constructor
* @param {Ember.EnumIterator|null} base the base Enumerable object to wrap
*/
constructor(base) {
if (!base) {
this._iterator = new NullObjectIterator();
} else if (base[Symbol.iterator]) {
this._iterator = base[Symbol.iterator]();
} else {
this._iterator = objectAtIterator(base);
}
}
/**
* @method next
* @return {Object} Iterator protocal with value and done properties
*/
next() {
return this._iterator.next();
}
/**
* @method [Symbol.iterator]
* @return {Iterator} a reference to this object
*/
[Symbol.iterator]() {
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment