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

[BUGFIX beta] Expose mechanism to detect if a property is a computed. #17938

Merged
merged 1 commit into from
Apr 23, 2019
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
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