Skip to content

Commit

Permalink
Merge pull request #18069 from emberjs/tracked/dependent-key-compat
Browse files Browse the repository at this point in the history
[FEATURE] Adds dependentKeyCompat
  • Loading branch information
rwjblue authored Jun 1, 2019
2 parents 0abb67e + 9e9adbc commit a9f2c52
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 3 deletions.
15 changes: 13 additions & 2 deletions packages/@ember/-internals/metal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ export {
PROPERTY_DID_CHANGE,
} from './lib/property_events';
export { defineProperty } from './lib/properties';
export { isElementDescriptor, nativeDescDecorator } from './lib/decorator';
export {
Decorator,
DecoratorPropertyDescriptor,
isElementDescriptor,
nativeDescDecorator,
} from './lib/decorator';
export {
descriptorForDecorator,
descriptorForProperty,
Expand All @@ -54,7 +59,13 @@ export { default as expandProperties } from './lib/expand_properties';
export { addObserver, activateObserver, removeObserver, flushAsyncObservers } from './lib/observer';
export { Mixin, aliasMethod, mixin, observer, applyMixin } from './lib/mixin';
export { default as inject, DEBUG_INJECTION_FUNCTIONS } from './lib/injected_property';
export { tagForProperty, tagFor, markObjectAsDirty, UNKNOWN_PROPERTY_TAG } from './lib/tags';
export {
tagForProperty,
tagFor,
markObjectAsDirty,
UNKNOWN_PROPERTY_TAG,
update,
} from './lib/tags';
export { default as runInTransaction, didRender, assertNotRendered } from './lib/transaction';
export { consume, Tracker, tracked, track } from './lib/tracked';

Expand Down
91 changes: 91 additions & 0 deletions packages/@ember/object/compat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Meta } from '@ember/-internals/meta';
import {
consume,
Decorator,
DecoratorPropertyDescriptor,
isElementDescriptor,
setClassicDecorator,
tagForProperty,
track,
update,
} from '@ember/-internals/metal';
import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features';
import { assert } from '@ember/debug';

let wrapGetterSetter = function(_target: object, key: string, desc: PropertyDescriptor) {
let { get: originalGet } = desc;

if (originalGet !== undefined) {
desc.get = function() {
let propertyTag = tagForProperty(this, key);
let ret;

let tag = track(() => {
ret = originalGet!.call(this);
});

update(propertyTag, tag);
consume(tag);

return ret;
};
}

return desc;
};

export function dependentKeyCompat(
target: object,
key: string,
desc: PropertyDescriptor
): PropertyDescriptor;
export function dependentKeyCompat(desc: { get?: Function; set?: Function }): Decorator;
export function dependentKeyCompat(
target: object | { get?: Function; set?: Function },
key?: string,
desc?: PropertyDescriptor
) {
assert(
'The dependentKeyCompat decorator can only be used if the tracked properties feature is enabled',
Boolean(EMBER_METAL_TRACKED_PROPERTIES)
);

if (!isElementDescriptor([target, key, desc])) {
desc = target as PropertyDescriptor;

let decorator = function(
target: object,
key: string,
_desc: DecoratorPropertyDescriptor,
_meta?: Meta,
isClassicDecorator?: boolean
) {
assert(
'The @dependentKeyCompat decorator may only be passed a method when used in classic classes. You should decorate getters/setters directly in native classes',
isClassicDecorator
);

assert(
'The dependentKeyCompat() decorator must be passed a getter or setter when used in classic classes',
desc !== null &&
typeof desc === 'object' &&
(typeof desc.get === 'function' || typeof desc.set === 'function')
);

return wrapGetterSetter(target, key, desc!);
};

setClassicDecorator(decorator);

return decorator as Decorator;
}

assert(
'The @dependentKeyCompat decorator must be applied to getters/setters when used in native classes',
(desc !== null && typeof desc!.get === 'function') || typeof desc!.set === 'function'
);

return wrapGetterSetter(target, key!, desc!);
}

setClassicDecorator(dependentKeyCompat as Decorator);
126 changes: 126 additions & 0 deletions packages/@ember/object/tests/computed/dependent-key-compat-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features';

import { Object as EmberObject } from '@ember/-internals/runtime';
import { computed, tracked, observer } from '@ember/-internals/metal';
import { dependentKeyCompat } from '../../compat';
import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers';

if (EMBER_METAL_TRACKED_PROPERTIES) {
moduleFor(
'dependentKeyCompat',
class extends AbstractTestCase {
'@test it works with computed properties'(assert) {
class Person {
@tracked firstName = 'Tom';
@tracked lastName = 'Dale';

@dependentKeyCompat
get givenName() {
return this.firstName;
}

@computed('givenName', 'lastName')
get fullName() {
return `${this.givenName} ${this.lastName}`;
}
}

let tom = new Person();

assert.equal(tom.fullName, 'Tom Dale');

tom.firstName = 'Thomas';

assert.equal(tom.fullName, 'Thomas Dale');
}

'@test it works with classic classes'(assert) {
let Person = EmberObject.extend({
firstName: tracked({ value: 'Tom' }),
lastName: tracked({ value: 'Dale' }),

givenName: dependentKeyCompat({
get() {
return this.firstName;
},
}),

fullName: computed('givenName', 'lastName', function() {
return `${this.givenName} ${this.lastName}`;
}),
});

let tom = Person.create();

assert.equal(tom.fullName, 'Tom Dale');

tom.firstName = 'Thomas';

assert.equal(tom.fullName, 'Thomas Dale');
}

async '@test it works with async observers'(assert) {
let count = 0;

let Person = EmberObject.extend({
firstName: tracked({ value: 'Tom' }),
lastName: tracked({ value: 'Dale' }),

givenName: dependentKeyCompat({
get() {
return this.firstName;
},
}),

givenNameObserver: observer({
dependentKeys: ['givenName'],
fn() {
count++;
},
sync: false,
}),
});

let tom = Person.create();

assert.equal(count, 0);

tom.firstName = 'Thomas';
await runLoopSettled();

assert.equal(count, 1);
}

'@test it does not work with sync observers'(assert) {
let count = 0;

let Person = EmberObject.extend({
firstName: tracked({ value: 'Tom' }),
lastName: tracked({ value: 'Dale' }),

givenName: dependentKeyCompat({
get() {
return this.firstName;
},
}),

givenNameObserver: observer({
dependentKeys: ['givenName'],
fn() {
count++;
},
sync: true,
}),
});

let tom = Person.create();

assert.equal(count, 0);

tom.firstName = 'Thomas';

assert.equal(count, 0);
}
}
);
}
2 changes: 2 additions & 0 deletions packages/ember/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import Service, { inject as injectService } from '@ember/service';

import { action } from '@ember/object';
import { dependentKeyCompat } from '@ember/object/compat';

import {
and,
Expand Down Expand Up @@ -456,6 +457,7 @@ Ember.RSVP = RSVP;
Ember.Namespace = Namespace;

Ember._action = action;
Ember._dependentKeyCompat = dependentKeyCompat;

computed.empty = empty;
computed.notEmpty = notEmpty;
Expand Down
9 changes: 8 additions & 1 deletion packages/ember/tests/reexports_test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import Ember from '../index';
import { FEATURES, EMBER_NATIVE_DECORATOR_SUPPORT } from '@ember/canary-features';
import {
FEATURES,
EMBER_NATIVE_DECORATOR_SUPPORT,
EMBER_METAL_TRACKED_PROPERTIES,
} from '@ember/canary-features';
import { confirmExport } from 'internal-test-helpers';
import { moduleFor, AbstractTestCase } from 'internal-test-helpers';
import { jQueryDisabled, jQuery } from '@ember/-internals/views';
Expand Down Expand Up @@ -279,6 +283,9 @@ let allExports = [
{ get: 'isNamespaceSearchDisabled', set: 'setNamespaceSearchDisabled' },
],
EMBER_NATIVE_DECORATOR_SUPPORT ? ['_action', '@ember/object', 'action'] : null,
EMBER_METAL_TRACKED_PROPERTIES
? ['_dependentKeyCompat', '@ember/object/compat', 'dependentKeyCompat']
: null,
['computed.empty', '@ember/object/computed', 'empty'],
['computed.notEmpty', '@ember/object/computed', 'notEmpty'],
['computed.none', '@ember/object/computed', 'none'],
Expand Down

0 comments on commit a9f2c52

Please sign in to comment.