Skip to content

Commit

Permalink
[BUGFIX beta] Expose mechanism to detect if a property is a computed. (
Browse files Browse the repository at this point in the history
…#17938)

[BUGFIX beta] Expose mechanism to detect if a property is a computed.
  • Loading branch information
rwjblue authored Apr 23, 2019
2 parents ed4fb96 + 10b6303 commit 2cc3573
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 7 deletions.
7 changes: 6 additions & 1 deletion packages/@ember/-internals/metal/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export { default as computed, _globalsComputed, ComputedProperty } from './lib/computed';
export {
default as computed,
isComputed,
_globalsComputed,
ComputedProperty,
} from './lib/computed';
export { getCacheFor, getCachedValueFor, peekCacheFor } from './lib/computed_cache';
export { default as alias } from './lib/alias';
export { deprecateProperty } from './lib/deprecate_property';
Expand Down
23 changes: 22 additions & 1 deletion packages/@ember/-internals/metal/lib/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ import {
makeComputedDecorator,
removeDependentKeys,
} from './decorator';
import { descriptorForDecorator, isClassicDecorator } from './descriptor_map';
import {
descriptorForDecorator,
descriptorForProperty,
isClassicDecorator,
} from './descriptor_map';
import expandProperties from './expand_properties';
import { defineProperty } from './properties';
import { notifyPropertyChange } from './property_events';
Expand Down Expand Up @@ -842,6 +846,23 @@ export function computed(
) as ComputedDecorator;
}

/**
Allows checking if a given property on an object is a computed property. For the most part,
this doesn't matter (you would normally just access the property directly and use its value),
but for some tooling specific scenarios (e.g. the ember-inspector) it is important to
differentiate if a property is a computed property or a "normal" property.
This will work on either a class's prototype or an instance itself.
@static
@method isComputed
@for @ember/debug
@private
*/
export function isComputed(obj: object, key: string): boolean {
return Boolean(descriptorForProperty(obj, key));
}

export const _globalsComputed = computed.bind(null);

export default computed;
8 changes: 4 additions & 4 deletions packages/@ember/-internals/metal/lib/descriptor_map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ const DECORATOR_DESCRIPTOR_MAP: WeakMap<
/**
Returns the CP descriptor assocaited with `obj` and `keyName`, if any.
@method descriptorFor
@method descriptorForProperty
@param {Object} obj the object to check
@param {String} keyName the key to check
@return {Descriptor}
@private
*/
export function descriptorForProperty(obj: object, keyName: string, _meta?: Meta | null) {
assert('Cannot call `descriptorFor` on null', obj !== null);
assert('Cannot call `descriptorFor` on undefined', obj !== undefined);
assert('Cannot call `descriptorForProperty` on null', obj !== null);
assert('Cannot call `descriptorForProperty` on undefined', obj !== undefined);
assert(
`Cannot call \`descriptorFor\` on ${typeof obj}`,
`Cannot call \`descriptorForProperty\` on ${typeof obj}`,
typeof obj === 'object' || typeof obj === 'function'
);

Expand Down
19 changes: 19 additions & 0 deletions packages/@ember/-internals/metal/tests/computed_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
getCachedValueFor,
defineProperty,
isClassicDecorator,
isComputed,
get,
set,
isWatching,
Expand All @@ -17,6 +18,24 @@ let obj, count;
moduleFor(
'computed',
class extends AbstractTestCase {
['@test isComputed is true for computed property on a factory'](assert) {
let Obj = EmberObject.extend({
foo: computed(function() {}),
});

Obj.proto(); // ensure the prototype is "collapsed" / merged

assert.ok(isComputed(Obj.prototype, 'foo'));
}

['@test isComputed is true for computed property on an instance'](assert) {
let obj = EmberObject.extend({
foo: computed(function() {}),
}).create();

assert.ok(isComputed(obj, 'foo'));
}

['@test computed property should be an instance of descriptor'](assert) {
assert.ok(isClassicDecorator(computed(function() {})));
}
Expand Down
1 change: 1 addition & 0 deletions packages/ember/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ Ember.Error = EmberError;
Ember.Debug = {
registerDeprecationHandler: EmberDebug.registerDeprecationHandler,
registerWarnHandler: EmberDebug.registerWarnHandler,
isComputed: metal.isComputed,
};

// ****@ember/instrumentation****
Expand Down
3 changes: 2 additions & 1 deletion tests/docs/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ module.exports = {
'deprecateProperty',
'deprecatingAlias',
'describe',
'descriptorFor',
'descriptorForProperty',
'deserialize',
'deserializeQueryParam',
'destroy',
Expand Down Expand Up @@ -299,6 +299,7 @@ module.exports = {
'isBlank',
'isBrowser',
'isClassicDecorator',
'isComputed',
'isDestroyed',
'isDestroying',
'isEmpty',
Expand Down

0 comments on commit 2cc3573

Please sign in to comment.