Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add more capabilities to createClassComputed #69

Merged
merged 1 commit into from
Apr 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,19 @@ import createClassComputed from 'ember-macro-helpers/create-class-computed';
import computed from 'ember-macro-helpers/computed';

export default createClassComputed(
// the first param is the key map
// you give an internal name to the keys coming in from your macro
// the first param is the observer list
// it refers to incoming keys
// the bool is whether a value change should recreate the macro
{
array: false,
key: true,
value: false
},
[
// the array key
false,

// the array property is dynamic, and is responsible for the macro being rewritten
true,

//
false
],
// the second param is the callback function where you create your computed property
// it is passed in the values of the properties you marked true above
key => {
Expand Down
2 changes: 1 addition & 1 deletion addon/-build-computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function parseComputedArgs(args) {
};
}

export function mapKeysToValues(keys, getValue, context) {
function mapKeysToValues(keys, getValue, context) {
return keys.map(key => getValue(context, key));
}

Expand Down
99 changes: 63 additions & 36 deletions addon/create-class-computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import { isNone } from 'ember-utils';
import get from 'ember-metal/get';
import { setProperties } from 'ember-metal/set';
import WeakMap from 'ember-weakmap';
import { mapKeysToValues } from './-build-computed';
import getValue from './get-value';
import collapseKeys from './collapse-keys';
import { collapseKeysWithMap } from './collapse-keys';
import flattenKeys from './flatten-keys';

const { defineProperty } = Ember;
Expand All @@ -31,63 +30,91 @@ function findOrCreatePropertyInstance(context, propertyClass, key) {

// let owner = getOwner(context);
property = propertyClass.create(/*owner.ownerInjection(), */{
_key: key,
_context: context
key,
context,
nonStrings: EmberObject.create()
});

propertiesForContext[key] = property;
return property;
}

const BaseClass = EmberObject.extend({
_computedDidChange: observer('_computed', function() {
this._context.notifyPropertyChange(this._key);
computedDidChange: observer('computed', function() {
this.context.notifyPropertyChange(this.key);
})
});

function createObserverClass(classKeysObj, macroGenerator) {
let classKeys = Object.keys(classKeysObj);
let observers = classKeys.filter(key => classKeysObj[key]);

function rewriteComputed() {
let values = mapKeysToValues(observers, getValue, this);
let cp = macroGenerator(...values);
defineProperty(this, '_computed', cp);
function resolveMappedLocation(key, i) {
if (typeof key === 'string') {
return `context.${key}`;
} else {
return `nonStrings.${i}`;
}

let classProperties = observers.reduce((properties, key) => {
properties[`${key}DidChange`] = observer(key, rewriteComputed);
return properties;
}, {});

return BaseClass.extend(classProperties, {
_onInit: on('init', rewriteComputed)
});
}

function createComputed(classKeysObj, ObserverClass) {
let classKeys = Object.keys(classKeysObj);

export default function(observerBools, macroGenerator) {
return function(...keys) {
let collapsedKeys = collapseKeys(keys);
let { collapsedKeys, keyMap } = collapseKeysWithMap(keys);

function getOriginalArrayDecorator(key, i) {
if (typeof key === 'string') {
let originalKey = keys[keyMap[i]];
if (originalKey.indexOf('.[]') !== -1 || originalKey.indexOf('.@each') !== -1) {
return originalKey;
}
}
return key;
}

let mappedKeys = [];

function rewriteComputed() {
let mappedWithResolvedOberverKeys = mappedKeys.map((key, i) => {
let shouldObserve = observerBools[i];
if (shouldObserve) {
key = getValue(this, key);
}
return key;
});

let cp = macroGenerator.apply(this, mappedWithResolvedOberverKeys);
defineProperty(this, 'computed', cp);
}

let classProperties = {};

collapsedKeys.forEach((key, i) => {
let shouldObserve = observerBools[i];
if (!shouldObserve) {
key = getOriginalArrayDecorator(key, i);
}

key = resolveMappedLocation(key, i);

mappedKeys.push(key);
if (shouldObserve) {
classProperties[`key${i}DidChange`] = observer(key, rewriteComputed);
}
});

let ObserverClass = BaseClass.extend(classProperties, {
onInit: on('init', rewriteComputed)
});

return computed(...flattenKeys(keys), function(key) {
let propertyInstance = findOrCreatePropertyInstance(this, ObserverClass, key);

let properties = collapsedKeys.reduce((properties, key, i) => {
properties[classKeys[i]] = getValue(this, key);
if (typeof key !== 'string') {
properties[i.toString()] = getValue(this, key);
}
return properties;
}, {});

setProperties(propertyInstance, properties);
setProperties(propertyInstance.nonStrings, properties);

return get(propertyInstance, '_computed');
return get(propertyInstance, 'computed');
}).readOnly();
};
}

export default function(classKeysObj, macroGenerator) {
let ObserverClass = createObserverClass(classKeysObj, macroGenerator);

return createComputed(classKeysObj, ObserverClass);
}
81 changes: 71 additions & 10 deletions tests/integration/create-class-computed-test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import createClassComputed from 'ember-macro-helpers/create-class-computed';
import normalizeArrayKey from 'ember-macro-helpers/normalize-array-key';
import computed from 'ember-macro-helpers/computed';
import raw from 'ember-macro-helpers/raw';
import { module, test } from 'qunit';
import EmberObject from 'ember-object';
import { A as emberA } from 'ember-array/utils';
import computed from 'ember-macro-helpers/computed';
import compute from 'ember-macro-test-helpers/compute';
import WeakMap from 'ember-weakmap';

let PROPERTIES;
let filterBy;

module('Integration | create class computed', {
beforeEach() {
PROPERTIES = new WeakMap();

filterBy = createClassComputed(
{
array: false,
key: true,
value: false
},
key => {
return computed(`array.@each.${key}`, 'value', (array, value) => {
[false, true, false],
function(array, key, value) {
if (!key) {
PROPERTIES.set(this, array.split('.').reverse()[0]);
}
return computed(normalizeArrayKey(array, [key]), value, function(array, value) {
if (array) {
return array.filterBy(key, value);
return array.filterBy(key || PROPERTIES.get(this), value);
}
});
}
Expand All @@ -44,7 +49,7 @@ test('it initially calculates correctly', function(assert) {
assert.equal(subject.get('computed.length'), 1);
});

test('it responds to array property value changes', function(assert) {
test('it responds to array property value changes internally', function(assert) {
let array = emberA([
EmberObject.create({ test: 'val1' }),
EmberObject.create({ test: 'val2' })
Expand All @@ -64,6 +69,62 @@ test('it responds to array property value changes', function(assert) {
assert.equal(subject.get('computed.length'), 2);
});

test('it responds to array property value changes externally', function(assert) {
let array = emberA([
EmberObject.create({ test: 'val1' }),
EmberObject.create({ test: 'val2' })
]);

let { subject } = compute({
computed: filterBy('[email protected]', 'key', 'value'),
properties: {
array,
value: 'val1'
}
});

array.set('1.test', 'val1');

assert.equal(subject.get('computed.length'), 2);
});

test('it responds to array property value changes using composing', function(assert) {
let array = emberA([
EmberObject.create({ test: 'val1' }),
EmberObject.create({ test: 'val2' })
]);

let { subject } = compute({
computed: filterBy(array, raw('test'), raw('val1'))
});

array.set('1.test', 'val1');

assert.equal(subject.get('computed.length'), 2);
});

test('it responds to property value changes using brace expansion', function(assert) {
let array = emberA([
EmberObject.create({ test: 'val1' }),
EmberObject.create({ test: 'val1' })
]);

let { subject } = compute({
computed: filterBy('obj.{array,key,value}'),
properties: {
obj: EmberObject.create({
array,
key: 'test',
value: 'val2'
})
}
});

subject.set('obj.value', 'val1');

assert.equal(subject.get('computed.length'), 2);
});

test('it responds to array length changes', function(assert) {
let array = emberA([
EmberObject.create({ test: 'val1' }),
Expand Down
Loading