Skip to content

Commit

Permalink
[BUGFIX beta] Adds assertions to computed macros
Browse files Browse the repository at this point in the history
Adds assertions that prevent users from using decorator macros
directly as decorators without passing parameters
  • Loading branch information
Chris Garrett committed Apr 5, 2019
1 parent 02925ad commit 7038da2
Show file tree
Hide file tree
Showing 4 changed files with 502 additions and 3 deletions.
6 changes: 6 additions & 0 deletions packages/@ember/-internals/metal/lib/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
addDependentKeys,
ComputedDescriptor,
Decorator,
isElementDescriptor,
makeComputedDecorator,
removeDependentKeys,
} from './decorator';
Expand All @@ -20,6 +21,11 @@ const CONSUMED = Object.freeze({});
export type AliasDecorator = Decorator & PropertyDecorator & AliasDecoratorImpl;

export default function alias(altKey: string): AliasDecorator {
assert(
'You attempted to use @alias as a decorator directly, but it requires a `altKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return makeComputedDecorator(new AliasedProperty(altKey), AliasDecoratorImpl) as AliasDecorator;
}

Expand Down
76 changes: 76 additions & 0 deletions packages/@ember/object/lib/computed/computed_macros.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
get,
set,
computed,
isElementDescriptor,
isEmpty,
isNone,
alias,
Expand Down Expand Up @@ -35,6 +36,11 @@ function expandPropertiesToArray(predicateName, properties) {

function generateComputedWithPredicate(name, predicate) {
return (...properties) => {
assert(
`You attempted to use @${name} as a decorator directly, but it requires at least one dependent key parameter`,
!isElementDescriptor(properties)
);

let dependentKeys = expandPropertiesToArray(name, properties);

let computedFunc = computed(...dependentKeys, function() {
Expand Down Expand Up @@ -112,6 +118,11 @@ function generateComputedWithPredicate(name, predicate) {
@public
*/
export function empty(dependentKey) {
assert(
'You attempted to use @empty as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(`${dependentKey}.length`, function() {
return isEmpty(get(this, dependentKey));
});
Expand Down Expand Up @@ -172,6 +183,11 @@ export function empty(dependentKey) {
@public
*/
export function notEmpty(dependentKey) {
assert(
'You attempted to use @notEmpty as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(`${dependentKey}.length`, function() {
return !isEmpty(get(this, dependentKey));
});
Expand Down Expand Up @@ -231,6 +247,11 @@ export function notEmpty(dependentKey) {
@public
*/
export function none(dependentKey) {
assert(
'You attempted to use @none as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return isNone(get(this, dependentKey));
});
Expand Down Expand Up @@ -287,6 +308,11 @@ export function none(dependentKey) {
@public
*/
export function not(dependentKey) {
assert(
'You attempted to use @not as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return !get(this, dependentKey);
});
Expand Down Expand Up @@ -355,6 +381,11 @@ export function not(dependentKey) {
@public
*/
export function bool(dependentKey) {
assert(
'You attempted to use @bool as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return Boolean(get(this, dependentKey));
});
Expand Down Expand Up @@ -417,6 +448,11 @@ export function bool(dependentKey) {
@public
*/
export function match(dependentKey, regexp) {
assert(
'You attempted to use @match as a decorator directly, but it requires `dependentKey` and `regexp` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
let value = get(this, dependentKey);
return regexp.test(value);
Expand Down Expand Up @@ -479,6 +515,11 @@ export function match(dependentKey, regexp) {
@public
*/
export function equal(dependentKey, value) {
assert(
'You attempted to use @equal as a decorator directly, but it requires `dependentKey` and `value` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return get(this, dependentKey) === value;
});
Expand Down Expand Up @@ -540,6 +581,11 @@ export function equal(dependentKey, value) {
@public
*/
export function gt(dependentKey, value) {
assert(
'You attempted to use @gt as a decorator directly, but it requires `dependentKey` and `value` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return get(this, dependentKey) > value;
});
Expand Down Expand Up @@ -601,6 +647,11 @@ export function gt(dependentKey, value) {
@public
*/
export function gte(dependentKey, value) {
assert(
'You attempted to use @gte as a decorator directly, but it requires `dependentKey` and `value` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return get(this, dependentKey) >= value;
});
Expand Down Expand Up @@ -662,6 +713,11 @@ export function gte(dependentKey, value) {
@public
*/
export function lt(dependentKey, value) {
assert(
'You attempted to use @lt as a decorator directly, but it requires `dependentKey` and `value` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return get(this, dependentKey) < value;
});
Expand Down Expand Up @@ -723,6 +779,11 @@ export function lt(dependentKey, value) {
@public
*/
export function lte(dependentKey, value) {
assert(
'You attempted to use @lte as a decorator directly, but it requires `dependentKey` and `value` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, function() {
return get(this, dependentKey) <= value;
});
Expand Down Expand Up @@ -991,6 +1052,11 @@ export const or = generateComputedWithPredicate('or', value => !value);
@public
*/
export function oneWay(dependentKey) {
assert(
'You attempted to use @oneWay as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return alias(dependentKey).oneWay();
}

Expand Down Expand Up @@ -1075,6 +1141,11 @@ export function oneWay(dependentKey) {
@public
*/
export function readOnly(dependentKey) {
assert(
'You attempted to use @readOnly as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return alias(dependentKey).readOnly();
}

Expand Down Expand Up @@ -1133,6 +1204,11 @@ export function readOnly(dependentKey) {
@public
*/
export function deprecatingAlias(dependentKey, options) {
assert(
'You attempted to use @deprecatingAlias as a decorator directly, but it requires `dependentKey` and `options` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return computed(dependentKey, {
get(key) {
deprecate(
Expand Down
72 changes: 69 additions & 3 deletions packages/@ember/object/lib/computed/reduce_computed_macros.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import { DEBUG } from '@glimmer/env';
import { assert } from '@ember/debug';
import {
get,
computed,
addObserver,
removeObserver,
computed,
get,
isElementDescriptor,
notifyPropertyChange,
removeObserver,
} from '@ember/-internals/metal';
import { compare, isArray, A as emberA, uniqBy as uniqByArray } from '@ember/-internals/runtime';

Expand Down Expand Up @@ -105,6 +106,11 @@ function multiArrayMacro(_dependentKeys, callback, name) {
@public
*/
export function sum(dependentKey) {
assert(
'You attempted to use @sum as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return reduceMacro(dependentKey, (sum, item) => sum + item, 0, 'sum');
}

Expand Down Expand Up @@ -200,6 +206,11 @@ export function sum(dependentKey) {
@public
*/
export function max(dependentKey) {
assert(
'You attempted to use @max as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return reduceMacro(dependentKey, (max, item) => Math.max(max, item), -Infinity, 'max');
}

Expand Down Expand Up @@ -294,6 +305,11 @@ export function max(dependentKey) {
@public
*/
export function min(dependentKey) {
assert(
'You attempted to use @min as a decorator directly, but it requires a `dependentKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return reduceMacro(dependentKey, (min, item) => Math.min(min, item), Infinity, 'min');
}

Expand Down Expand Up @@ -392,6 +408,11 @@ export function min(dependentKey) {
@public
*/
export function map(dependentKey, additionalDependentKeys, callback) {
assert(
'You attempted to use @map as a decorator directly, but it requires atleast `dependentKey` and `callback` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

if (callback === undefined && typeof additionalDependentKeys === 'function') {
callback = additionalDependentKeys;
additionalDependentKeys = [];
Expand Down Expand Up @@ -496,6 +517,11 @@ export function map(dependentKey, additionalDependentKeys, callback) {
@public
*/
export function mapBy(dependentKey, propertyKey) {
assert(
'You attempted to use @mapBy as a decorator directly, but it requires `dependentKey` and `propertyKey` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

assert(
'`computed.mapBy` expects a property string for its second argument, ' +
'perhaps you meant to use "map"',
Expand Down Expand Up @@ -638,6 +664,11 @@ export function mapBy(dependentKey, propertyKey) {
@public
*/
export function filter(dependentKey, additionalDependentKeys, callback) {
assert(
'You attempted to use @filter as a decorator directly, but it requires atleast `dependentKey` and `callback` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

if (callback === undefined && typeof additionalDependentKeys === 'function') {
callback = additionalDependentKeys;
additionalDependentKeys = [];
Expand Down Expand Up @@ -715,6 +746,11 @@ export function filter(dependentKey, additionalDependentKeys, callback) {
@public
*/
export function filterBy(dependentKey, propertyKey, value) {
assert(
'You attempted to use @filterBy as a decorator directly, but it requires atleast `dependentKey` and `propertyKey` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

assert(
`Dependent key passed to \`computed.filterBy\` shouldn't contain brace expanding pattern.`,
!/[\[\]\{\}]/g.test(dependentKey)
Expand Down Expand Up @@ -789,6 +825,11 @@ export function filterBy(dependentKey, propertyKey, value) {
@public
*/
export function uniq(...args) {
assert(
'You attempted to use @uniq/@union as a decorator directly, but it requires atleast one dependent key parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return multiArrayMacro(
args,
function(dependentKeys) {
Expand Down Expand Up @@ -873,6 +914,11 @@ export function uniq(...args) {
@public
*/
export function uniqBy(dependentKey, propertyKey) {
assert(
'You attempted to use @uniqBy as a decorator directly, but it requires `dependentKey` and `propertyKey` parameters',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

assert(
`Dependent key passed to \`computed.uniqBy\` shouldn't contain brace expanding pattern.`,
!/[\[\]\{\}]/g.test(dependentKey)
Expand Down Expand Up @@ -1013,6 +1059,11 @@ export let union = uniq;
@public
*/
export function intersect(...args) {
assert(
'You attempted to use @intersect as a decorator directly, but it requires atleast one dependent key parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return multiArrayMacro(
args,
function(dependentKeys) {
Expand Down Expand Up @@ -1115,6 +1166,11 @@ export function intersect(...args) {
@public
*/
export function setDiff(setAProperty, setBProperty) {
assert(
'You attempted to use @setDiff as a decorator directly, but it requires atleast one dependent key parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

assert('`computed.setDiff` requires exactly two dependent arrays.', arguments.length === 2);
assert(
`Dependent keys passed to \`computed.setDiff\` shouldn't contain brace expanding pattern.`,
Expand Down Expand Up @@ -1187,6 +1243,11 @@ export function setDiff(setAProperty, setBProperty) {
@public
*/
export function collect(...dependentKeys) {
assert(
'You attempted to use @collect as a decorator directly, but it requires atleast one dependent key parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

return multiArrayMacro(
dependentKeys,
function() {
Expand Down Expand Up @@ -1373,6 +1434,11 @@ export function collect(...dependentKeys) {
@public
*/
export function sort(itemsKey, additionalDependentKeys, sortDefinition) {
assert(
'You attempted to use @sort as a decorator directly, but it requires atleast an `itemsKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);

if (DEBUG) {
let argumentsValid = false;

Expand Down
Loading

0 comments on commit 7038da2

Please sign in to comment.