diff --git a/packages/@ember/-internals/metal/lib/mixin.ts b/packages/@ember/-internals/metal/lib/mixin.ts index 8d1326c0e1c..d59c74865e3 100644 --- a/packages/@ember/-internals/metal/lib/mixin.ts +++ b/packages/@ember/-internals/metal/lib/mixin.ts @@ -5,6 +5,7 @@ import { Meta, meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { getListeners, getObservers, + getOwnPropertyDescriptors, guidFor, makeArray, NAME_KEY, @@ -22,7 +23,7 @@ import { ComputedPropertyGetter, ComputedPropertySetter, } from './computed'; -import { makeComputedDecorator } from './decorator'; +import { makeComputedDecorator, nativeDescDecorator } from './decorator'; import { descriptorForDecorator, descriptorForProperty, @@ -50,6 +51,36 @@ function isMethod(obj: any): boolean { ); } +function isAccessor(desc: PropertyDescriptor) { + return typeof desc.get === 'function' || typeof desc.set === 'function'; +} + +function extractAccessors(properties: { [key: string]: any } | undefined) { + if (properties !== undefined) { + let descriptors = getOwnPropertyDescriptors(properties); + let keys = Object.keys(descriptors); + let hasAccessors = keys.some(key => isAccessor(descriptors[key])); + + if (hasAccessors) { + let extracted = {}; + + keys.forEach(key => { + let descriptor = descriptors[key]; + + if (isAccessor(descriptor)) { + extracted[key] = nativeDescDecorator(descriptor); + } else { + extracted[key] = properties[key]; + } + }); + + return extracted; + } + } + + return properties; +} + const CONTINUE: MixinLike = {}; function mixinProperties(mixinsMeta: Meta, mixin: T): MixinLike { @@ -550,7 +581,7 @@ export default class Mixin { _without: any[] | undefined; constructor(mixins: Mixin[] | undefined, properties?: { [key: string]: any }) { - this.properties = properties; + this.properties = extractAccessors(properties); this.mixins = buildMixinsArray(mixins); this.ownerConstructor = undefined; this._without = undefined; diff --git a/packages/@ember/-internals/metal/tests/mixin/accessor_test.js b/packages/@ember/-internals/metal/tests/mixin/accessor_test.js new file mode 100644 index 00000000000..5bdfd07031a --- /dev/null +++ b/packages/@ember/-internals/metal/tests/mixin/accessor_test.js @@ -0,0 +1,38 @@ +import { Mixin } from '../..'; +import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; + +moduleFor( + 'Mixin Accessors', + class extends AbstractTestCase { + ['@test works with getters'](assert) { + let count = 0; + + let MixinA = Mixin.create({ + get prop() { + return count++; + }, + }); + + let obj = {}; + MixinA.apply(obj); + + assert.equal(obj.prop, 0, 'getter defined correctly'); + assert.equal(obj.prop, 1, 'getter defined correctly'); + } + + ['@test works with setters'](assert) { + let MixinA = Mixin.create({ + set prop(value) { + this._prop = value + 1; + }, + }); + + let obj = {}; + MixinA.apply(obj); + + obj.prop = 0; + + assert.equal(obj._prop, 1, 'setter defined correctly'); + } + } +); diff --git a/packages/@ember/-internals/utils/index.ts b/packages/@ember/-internals/utils/index.ts index 8adac68d3d4..8c09ac3e8eb 100644 --- a/packages/@ember/-internals/utils/index.ts +++ b/packages/@ember/-internals/utils/index.ts @@ -10,6 +10,7 @@ */ export { default as symbol, isInternalSymbol } from './lib/symbol'; export { default as dictionary } from './lib/dictionary'; +export { default as getOwnPropertyDescriptors } from './lib/get-own-property-descriptors'; export { uuid, GUID_KEY, generateGuid, guidFor } from './lib/guid'; export { default as intern } from './lib/intern'; export { diff --git a/packages/@ember/-internals/utils/lib/get-own-property-descriptors.ts b/packages/@ember/-internals/utils/lib/get-own-property-descriptors.ts new file mode 100644 index 00000000000..30ddff39dd5 --- /dev/null +++ b/packages/@ember/-internals/utils/lib/get-own-property-descriptors.ts @@ -0,0 +1,17 @@ +let getOwnPropertyDescriptors: (obj: { [x: string]: any }) => { [x: string]: PropertyDescriptor }; + +if (Object.getOwnPropertyDescriptors !== undefined) { + getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; +} else { + getOwnPropertyDescriptors = function(obj: object) { + let descriptors = {}; + + Object.keys(obj).forEach(key => { + descriptors[key] = Object.getOwnPropertyDescriptor(obj, key); + }); + + return descriptors; + }; +} + +export default getOwnPropertyDescriptors;