Skip to content

Commit 004c2a1

Browse files
authored
Merge pull request #6411 from emberjs/cherry-pick-6339-beta
[BUGFIX beta] Adds support for parenless attr, belongsTo, and hasMany (#6339)
2 parents a2e477f + 7223952 commit 004c2a1

File tree

7 files changed

+113
-3
lines changed

7 files changed

+113
-3
lines changed

packages/-ember-data/tests/unit/model-test.js

+37
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import JSONSerializer from '@ember-data/serializer/json';
1414
import { attr as DSattr } from '@ember-data/model';
1515
import { recordDataFor } from 'ember-data/-private';
1616
import { attr, hasMany, belongsTo } from '@ember-data/model';
17+
import { gte } from 'ember-compatibility-helpers';
1718

1819
module('unit/model - Model', function(hooks) {
1920
setupTest(hooks);
@@ -1470,6 +1471,42 @@ module('unit/model - Model', function(hooks) {
14701471

14711472
await cat.save();
14721473
});
1474+
1475+
if (gte('3.10.0')) {
1476+
test('@attr decorator works without parens', async function(assert) {
1477+
assert.expect(1);
1478+
let cat;
1479+
1480+
class Mascot extends Model {
1481+
@attr name;
1482+
}
1483+
1484+
this.owner.register('model:mascot', Mascot);
1485+
adapter.updateRecord = function() {
1486+
assert.deepEqual(cat.changedAttributes(), {
1487+
name: ['Argon', 'Helia'],
1488+
});
1489+
1490+
return { data: { id: '1', type: 'mascot' } };
1491+
};
1492+
1493+
cat = store.push({
1494+
data: {
1495+
type: 'mascot',
1496+
id: '1',
1497+
attributes: {
1498+
name: 'Argon',
1499+
},
1500+
},
1501+
});
1502+
1503+
cat.setProperties({
1504+
name: 'Helia',
1505+
});
1506+
1507+
await cat.save();
1508+
});
1509+
}
14731510
});
14741511

14751512
module('Misc', function() {

packages/-ember-data/tests/unit/model/relationships-test.js

+32
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { module, test } from 'qunit';
33
import Model from '@ember-data/model';
44
import { hasMany, belongsTo } from '@ember-data/model';
55
import { get } from '@ember/object';
6+
import { gte } from 'ember-compatibility-helpers';
67

78
class Person extends Model {
89
@hasMany('occupation', { async: false }) occupations;
@@ -125,4 +126,35 @@ module('[@ember-data/model] unit - relationships', function(hooks) {
125126

126127
assert.equal(relationship.meta.name, 'streamItems', 'relationship name has not been changed');
127128
});
129+
130+
if (gte('3.10.0')) {
131+
test('decorators works without parens', function(assert) {
132+
let store;
133+
let { owner } = this;
134+
135+
class StreamItem extends Model {
136+
@belongsTo user;
137+
}
138+
139+
class User extends Model {
140+
@hasMany streamItems;
141+
}
142+
143+
owner.unregister('model:user');
144+
owner.register('model:stream-item', StreamItem);
145+
owner.register('model:user', User);
146+
147+
store = owner.lookup('service:store');
148+
149+
let user = store.modelFor('user');
150+
151+
const relationships = get(user, 'relationships');
152+
153+
assert.ok(relationships.has('stream-item'), 'relationship key has been normalized');
154+
155+
const relationship = relationships.get('stream-item')[0];
156+
157+
assert.equal(relationship.meta.name, 'streamItems', 'relationship name has not been changed');
158+
});
159+
}
128160
});

packages/model/addon/-private/attr.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { assert } from '@ember/debug';
33
import { DEBUG } from '@glimmer/env';
44
import { recordDataFor } from '@ember-data/store/-private';
55
import { RECORD_DATA_ERRORS } from '@ember-data/canary-features';
6+
import { computedMacroWithOptionalParams } from './util';
67

78
/**
89
@module @ember-data/model
@@ -107,7 +108,7 @@ function hasValue(internalModel, key) {
107108
@param {Object} options a hash of options
108109
@return {Attribute}
109110
*/
110-
export default function attr(type, options) {
111+
function attr(type, options) {
111112
if (typeof type === 'object') {
112113
options = type;
113114
type = undefined;
@@ -160,3 +161,5 @@ export default function attr(type, options) {
160161
},
161162
}).meta(meta);
162163
}
164+
165+
export default computedMacroWithOptionalParams(attr);

packages/model/addon/-private/belongs-to.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { computed } from '@ember/object';
22
import { assert, warn, inspect } from '@ember/debug';
33
import { normalizeModelName } from '@ember-data/store';
44
import { DEBUG } from '@glimmer/env';
5+
import { computedMacroWithOptionalParams } from './util';
56

67
/**
78
@module @ember-data/model
@@ -103,7 +104,7 @@ import { DEBUG } from '@glimmer/env';
103104
@param {Object} options (optional) a hash of options
104105
@return {Ember.computed} relationship
105106
*/
106-
export default function belongsTo(modelName, options) {
107+
function belongsTo(modelName, options) {
107108
let opts, userEnteredModelName;
108109
if (typeof modelName === 'object') {
109110
opts = modelName;
@@ -180,3 +181,5 @@ export default function belongsTo(modelName, options) {
180181
},
181182
}).meta(meta);
182183
}
184+
185+
export default computedMacroWithOptionalParams(belongsTo);

packages/model/addon/-private/has-many.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { computed } from '@ember/object';
55
import { assert, inspect } from '@ember/debug';
66
import { normalizeModelName } from '@ember-data/store';
77
import { DEBUG } from '@glimmer/env';
8+
import { computedMacroWithOptionalParams } from './util';
89

910
/**
1011
`hasMany` is used to define One-To-Many and Many-To-Many
@@ -141,7 +142,7 @@ import { DEBUG } from '@glimmer/env';
141142
@param {Object} options (optional) a hash of options
142143
@return {Ember.computed} relationship
143144
*/
144-
export default function hasMany(type, options) {
145+
function hasMany(type, options) {
145146
if (typeof type === 'object') {
146147
options = type;
147148
type = undefined;
@@ -199,3 +200,5 @@ export default function hasMany(type, options) {
199200
},
200201
}).meta(meta);
201202
}
203+
204+
export default computedMacroWithOptionalParams(hasMany);

packages/model/addon/-private/util.ts

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { gte } from 'ember-compatibility-helpers';
2+
3+
export type DecoratorPropertyDescriptor = PropertyDescriptor & { initializer?: any } | undefined;
4+
5+
export function isElementDescriptor(args: any[]): args is [object, string, DecoratorPropertyDescriptor] {
6+
let [maybeTarget, maybeKey, maybeDesc] = args;
7+
8+
return (
9+
// Ensure we have the right number of args
10+
args.length === 3 &&
11+
// Make sure the target is a class or object (prototype)
12+
(typeof maybeTarget === 'function' || (typeof maybeTarget === 'object' && maybeTarget !== null)) &&
13+
// Make sure the key is a string
14+
typeof maybeKey === 'string' &&
15+
// Make sure the descriptor is the right shape
16+
((typeof maybeDesc === 'object' &&
17+
maybeDesc !== null &&
18+
'enumerable' in maybeDesc &&
19+
'configurable' in maybeDesc) ||
20+
// TS compatibility
21+
maybeDesc === undefined)
22+
);
23+
}
24+
25+
export function computedMacroWithOptionalParams(fn) {
26+
if (gte('3.10.0')) {
27+
return (...maybeDesc: any[]) => (isElementDescriptor(maybeDesc) ? fn()(...maybeDesc) : fn(...maybeDesc));
28+
} else {
29+
return fn;
30+
}
31+
}

packages/model/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@ember-data/-build-infra": "3.13.0-beta.1",
2525
"@ember-data/store": "3.13.0-beta.1",
2626
"ember-cli-babel": "^7.8.0",
27+
"ember-compatibility-helpers": "^1.2.0",
2728
"ember-cli-string-utils": "^1.1.0",
2829
"ember-cli-test-info": "^1.0.0",
2930
"ember-cli-typescript": "^2.0.2",

0 commit comments

Comments
 (0)