Skip to content

Commit

Permalink
Merge pull request #17710 from emberjs/bugfix/allow-accessors-in-mixins
Browse files Browse the repository at this point in the history
[BUGFIX beta] Allow accessors in mixins
  • Loading branch information
rwjblue committed Mar 8, 2019
2 parents 4181da6 + 8bc6b60 commit d1506dc
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 2 deletions.
35 changes: 33 additions & 2 deletions packages/@ember/-internals/metal/lib/mixin.ts
Expand Up @@ -5,6 +5,7 @@ import { Meta, meta as metaFor, peekMeta } from '@ember/-internals/meta';
import {
getListeners,
getObservers,
getOwnPropertyDescriptors,
guidFor,
makeArray,
NAME_KEY,
Expand All @@ -22,7 +23,7 @@ import {
ComputedPropertyGetter,
ComputedPropertySetter,
} from './computed';
import { makeComputedDecorator } from './decorator';
import { makeComputedDecorator, nativeDescDecorator } from './decorator';
import {
descriptorForDecorator,
descriptorForProperty,
Expand Down Expand Up @@ -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<T extends MixinLike>(mixinsMeta: Meta, mixin: T): MixinLike {
Expand Down Expand Up @@ -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;
Expand Down
38 changes: 38 additions & 0 deletions 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');
}
}
);
1 change: 1 addition & 0 deletions packages/@ember/-internals/utils/index.ts
Expand Up @@ -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 {
Expand Down
@@ -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;

0 comments on commit d1506dc

Please sign in to comment.