diff --git a/packages/@ember/-internals/metal/index.ts b/packages/@ember/-internals/metal/index.ts index 62c0c0d71e8..789ec9e540b 100644 --- a/packages/@ember/-internals/metal/index.ts +++ b/packages/@ember/-internals/metal/index.ts @@ -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'; diff --git a/packages/@ember/-internals/metal/lib/computed.ts b/packages/@ember/-internals/metal/lib/computed.ts index 02a2d2d25c4..f48b247b9c9 100644 --- a/packages/@ember/-internals/metal/lib/computed.ts +++ b/packages/@ember/-internals/metal/lib/computed.ts @@ -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'; @@ -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; diff --git a/packages/@ember/-internals/metal/lib/descriptor_map.ts b/packages/@ember/-internals/metal/lib/descriptor_map.ts index dcc5fd5796e..3e470ebbf6e 100644 --- a/packages/@ember/-internals/metal/lib/descriptor_map.ts +++ b/packages/@ember/-internals/metal/lib/descriptor_map.ts @@ -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' ); diff --git a/packages/@ember/-internals/metal/tests/computed_test.js b/packages/@ember/-internals/metal/tests/computed_test.js index 4027c8136a7..7c6216613f6 100644 --- a/packages/@ember/-internals/metal/tests/computed_test.js +++ b/packages/@ember/-internals/metal/tests/computed_test.js @@ -4,6 +4,7 @@ import { getCachedValueFor, defineProperty, isClassicDecorator, + isComputed, get, set, isWatching, @@ -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() {}))); } diff --git a/packages/ember/index.js b/packages/ember/index.js index 3d2f063b5e6..129a9eed37b 100644 --- a/packages/ember/index.js +++ b/packages/ember/index.js @@ -227,6 +227,7 @@ Ember.Error = EmberError; Ember.Debug = { registerDeprecationHandler: EmberDebug.registerDeprecationHandler, registerWarnHandler: EmberDebug.registerWarnHandler, + isComputed: metal.isComputed, }; // ****@ember/instrumentation**** diff --git a/tests/docs/expected.js b/tests/docs/expected.js index 58d6371f883..4666e7965e2 100644 --- a/tests/docs/expected.js +++ b/tests/docs/expected.js @@ -167,7 +167,7 @@ module.exports = { 'deprecateProperty', 'deprecatingAlias', 'describe', - 'descriptorFor', + 'descriptorForProperty', 'deserialize', 'deserializeQueryParam', 'destroy', @@ -299,6 +299,7 @@ module.exports = { 'isBlank', 'isBrowser', 'isClassicDecorator', + 'isComputed', 'isDestroyed', 'isDestroying', 'isEmpty',