Skip to content

Commit e005411

Browse files
dwickerniStefoknownasilyaVincentMolinie
authored
Fix #316 polymorphic create fragment (#483)
* FragmentArray.createFragment should respect `type` in polymorphic arrays #316 * Remove unused test * Update tests/unit/polymorphic_test.js typo Co-authored-by: Vincent Molinié <[email protected]> * refactor: convert tests to modern syntax * fix attribute name in test * fix test failure * fix FragmentArray property documentation FragmentArray#type was renamed to modelName FragmentArray#options are no longer used * reformat code * ensure that the fragment owner is set * add failing test for createRecord * fix createRecord for polymorphic fragment arrays --------- Co-authored-by: Stefan Fochler <[email protected]> Co-authored-by: Ilya Radchenko <[email protected]> Co-authored-by: Vincent Molinié <[email protected]>
1 parent c9f6ca5 commit e005411

File tree

5 files changed

+151
-11
lines changed

5 files changed

+151
-11
lines changed

addon/array/fragment.js

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@ const FragmentArray = StatefulArray.extend({
2222
/**
2323
The type of fragments the array contains
2424
25-
@property type
25+
@property modelName
2626
@private
2727
@type {String}
2828
*/
29-
type: null,
30-
31-
options: null,
29+
modelName: null,
3230

3331
objectAt(index) {
3432
const recordData = this._super(index);
@@ -55,9 +53,7 @@ const FragmentArray = StatefulArray.extend({
5553
existing.setProperties(data);
5654
return recordDataFor(existing);
5755
}
58-
const fragment = this.store.createFragment(this.modelName, data);
59-
setFragmentOwner(fragment, this.recordData, this.key);
60-
return recordDataFor(fragment);
56+
return this.recordData._newFragmentRecordDataForKey(this.key, data);
6157
},
6258

6359
/**
@@ -142,15 +138,18 @@ const FragmentArray = StatefulArray.extend({
142138

143139
/**
144140
Creates a new fragment of the fragment array's type and adds it to the end
145-
of the fragment array
141+
of the fragment array.
146142
147143
@method createFragment
148144
@param {MF.Fragment} fragment
149145
@return {MF.Fragment} the newly added fragment
150146
*/
151147
createFragment(props) {
152-
const fragment = this.store.createFragment(this.modelName, props);
153-
setFragmentOwner(fragment, this.recordData, this.key);
148+
const recordData = this.recordData._newFragmentRecordDataForKey(
149+
this.key,
150+
props
151+
);
152+
const fragment = recordData._fragmentGetRecord();
154153
return this.pushObject(fragment);
155154
},
156155
});

addon/record-data.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,15 @@ export default class FragmentRecordData extends RecordData {
600600
this._fragmentOwner = { recordData, key };
601601
}
602602

603+
_newFragmentRecordDataForKey(key, attributes) {
604+
const behavior = this._fragmentBehavior[key];
605+
assert(
606+
`Attribute '${key}' for model '${this.modelName}' must be a fragment`,
607+
behavior != null
608+
);
609+
return this._newFragmentRecordData(behavior.definition, attributes);
610+
}
611+
603612
_newFragmentRecordData(definition, attributes) {
604613
const type = getActualFragmentType(
605614
definition.modelName,

tests/dummy/app/models/animal.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import Fragment from 'ember-data-model-fragments/fragment';
2+
import { fragmentOwner } from 'ember-data-model-fragments/attributes';
23
import { attr } from '@ember-data/model';
34

45
export default class Animal extends Fragment {
56
@attr('string') name;
7+
@fragmentOwner() zoo;
68
}

tests/dummy/app/models/component.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Model, { attr } from '@ember-data/model';
2-
import { fragment } from 'ember-data-model-fragments/attributes';
2+
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';
33

44
export default class Component extends Model {
55
@attr('string') name;
@@ -9,4 +9,10 @@ export default class Component extends Model {
99
typeKey: (data, owner) => `component-options-${owner.type}`,
1010
})
1111
options;
12+
13+
@fragmentArray('component-options', {
14+
polymorphic: true,
15+
typeKey: (data, owner) => `component-options-${owner.type}`,
16+
})
17+
optionsHistory;
1218
}

tests/unit/polymorphic_test.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { setupApplicationTest } from '../helpers';
33
import Animal from 'dummy/models/animal';
44
import Lion from 'dummy/models/lion';
55
import Elephant from 'dummy/models/elephant';
6+
import ComponentOptionsText from 'dummy/models/component-options-text';
67
let store, zoo;
78

89
module('unit - Polymorphism', function (hooks) {
@@ -83,4 +84,127 @@ module('unit - Polymorphism', function (hooks) {
8384
assert.ok(second instanceof Elephant);
8485
assert.equal(second.trunkLength, 4, "elephant's trunk length is correct");
8586
});
87+
88+
test("fragment array's createFragment supports polymorphism with string typeKey", async function (assert) {
89+
store.push({
90+
data: {
91+
type: 'zoo',
92+
id: 1,
93+
attributes: zoo,
94+
},
95+
});
96+
97+
const record = await store.find('zoo', 1);
98+
const animals = record.animals;
99+
100+
const newLion = animals.createFragment({
101+
$type: 'lion',
102+
name: 'Alex',
103+
hasManes: 'true',
104+
});
105+
106+
assert.ok(newLion instanceof Animal, 'new lion is an animal');
107+
assert.equal(newLion.name, 'Alex', "new animal's name is correct");
108+
assert.ok(newLion instanceof Lion, 'new lion is a lion');
109+
assert.ok(newLion.hasManes, 'lion has manes');
110+
assert.strictEqual(newLion.zoo, record, 'set the fragment owner');
111+
112+
const newElephant = animals.createFragment({
113+
$type: 'elephant',
114+
name: 'Heffalump',
115+
trunkLength: 7,
116+
});
117+
118+
assert.ok(newElephant instanceof Animal, 'new elephant is an animal');
119+
assert.equal(newElephant.name, 'Heffalump', "new animal's name is correct");
120+
assert.ok(newElephant instanceof Elephant, 'new elephant is an elephant');
121+
assert.equal(
122+
newElephant.trunkLength,
123+
7,
124+
"elephant's trunk length is correct"
125+
);
126+
assert.strictEqual(newElephant.zoo, record, 'set the fragment owner');
127+
});
128+
129+
test("fragment array's createFragment supports polymorphism with function typeKey", async function (assert) {
130+
store.push({
131+
data: {
132+
type: 'component',
133+
id: 1,
134+
attributes: {
135+
type: 'text',
136+
optionsHistory: [],
137+
},
138+
},
139+
});
140+
141+
const component = await store.find('component', 1);
142+
const textOptions = component.optionsHistory.createFragment({
143+
fontFamily: 'Verdana',
144+
fontSize: 12,
145+
});
146+
147+
assert.ok(
148+
textOptions instanceof ComponentOptionsText,
149+
'options is ComponentOptionsText'
150+
);
151+
assert.equal(
152+
textOptions.fontFamily,
153+
'Verdana',
154+
'options has correct fontFamily attribute'
155+
);
156+
assert.equal(
157+
textOptions.fontSize,
158+
12,
159+
'options has correct fontSize attribute'
160+
);
161+
assert.equal(
162+
component.optionsHistory.length,
163+
1,
164+
'fragment object was added to fragment array'
165+
);
166+
});
167+
168+
test('createRecord supports polymorphic typeKey for fragment and fragment arrays', async function (assert) {
169+
const zoo = store.createRecord('zoo', {
170+
star: {
171+
$type: 'lion',
172+
name: 'Mittens',
173+
hasManes: true,
174+
},
175+
animals: [
176+
{
177+
$type: 'lion',
178+
name: 'Alex',
179+
hasManes: false,
180+
},
181+
{
182+
$type: 'elephant',
183+
name: 'Heffalump',
184+
trunkLength: 7,
185+
},
186+
],
187+
});
188+
189+
const star = zoo.star;
190+
assert.ok(star instanceof Lion, 'star is a lion');
191+
assert.strictEqual(star.name, 'Mittens', 'star name is correct');
192+
assert.strictEqual(star.hasManes, true, 'star has manes');
193+
assert.strictEqual(star.zoo, zoo, 'star fragment owner is correct');
194+
195+
const animals = zoo.animals;
196+
assert.strictEqual(animals.length, 2);
197+
198+
const lion = animals.firstObject;
199+
assert.ok(lion instanceof Lion, 'first animal is a lion');
200+
assert.strictEqual(lion.name, 'Alex', 'lion name is correct');
201+
assert.false(lion.hasManes, 'lion does not have manes');
202+
assert.strictEqual(lion.zoo, zoo, 'lion fragment owner is correct');
203+
204+
const elephant = animals.lastObject;
205+
assert.ok(elephant instanceof Elephant, 'second animal is an elephant');
206+
assert.strictEqual(elephant.name, 'Heffalump', 'elephant name is correct');
207+
assert.strictEqual(elephant.trunkLength, 7, 'trunk length is correct');
208+
assert.strictEqual(elephant.zoo, zoo, 'elephant fragment owner is correct');
209+
});
86210
});

0 commit comments

Comments
 (0)