Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #316 polymorphic create fragment #483

19 changes: 9 additions & 10 deletions addon/array/fragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,11 @@ const FragmentArray = StatefulArray.extend({
/**
The type of fragments the array contains

@property type
@property modelName
@private
@type {String}
*/
type: null,

options: null,
modelName: null,

objectAt(index) {
const recordData = this._super(index);
Expand All @@ -55,9 +53,7 @@ const FragmentArray = StatefulArray.extend({
existing.setProperties(data);
return recordDataFor(existing);
}
const fragment = this.store.createFragment(this.modelName, data);
setFragmentOwner(fragment, this.recordData, this.key);
return recordDataFor(fragment);
return this.recordData._newFragmentRecordDataForKey(this.key, data);
},

/**
Expand Down Expand Up @@ -142,15 +138,18 @@ const FragmentArray = StatefulArray.extend({

/**
Creates a new fragment of the fragment array's type and adds it to the end
of the fragment array
of the fragment array.

@method createFragment
@param {MF.Fragment} fragment
@return {MF.Fragment} the newly added fragment
*/
createFragment(props) {
const fragment = this.store.createFragment(this.modelName, props);
setFragmentOwner(fragment, this.recordData, this.key);
const recordData = this.recordData._newFragmentRecordDataForKey(
this.key,
props
);
const fragment = recordData._fragmentGetRecord();
return this.pushObject(fragment);
},
});
Expand Down
9 changes: 9 additions & 0 deletions addon/record-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,15 @@ export default class FragmentRecordData extends RecordData {
this._fragmentOwner = { recordData, key };
}

_newFragmentRecordDataForKey(key, attributes) {
const behavior = this._fragmentBehavior[key];
assert(
`Attribute '${key}' for model '${this.modelName}' must be a fragment`,
behavior != null
);
return this._newFragmentRecordData(behavior.definition, attributes);
}

_newFragmentRecordData(definition, attributes) {
const type = getActualFragmentType(
definition.modelName,
Expand Down
2 changes: 2 additions & 0 deletions tests/dummy/app/models/animal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Fragment from 'ember-data-model-fragments/fragment';
import { fragmentOwner } from 'ember-data-model-fragments/attributes';
import { attr } from '@ember-data/model';

export default class Animal extends Fragment {
@attr('string') name;
@fragmentOwner() zoo;
}
8 changes: 7 additions & 1 deletion tests/dummy/app/models/component.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Model, { attr } from '@ember-data/model';
import { fragment } from 'ember-data-model-fragments/attributes';
import { fragment, fragmentArray } from 'ember-data-model-fragments/attributes';

export default class Component extends Model {
@attr('string') name;
Expand All @@ -9,4 +9,10 @@ export default class Component extends Model {
typeKey: (data, owner) => `component-options-${owner.type}`,
})
options;

@fragmentArray('component-options', {
polymorphic: true,
typeKey: (data, owner) => `component-options-${owner.type}`,
})
optionsHistory;
}
124 changes: 124 additions & 0 deletions tests/unit/polymorphic_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { setupApplicationTest } from '../helpers';
import Animal from 'dummy/models/animal';
import Lion from 'dummy/models/lion';
import Elephant from 'dummy/models/elephant';
import ComponentOptionsText from 'dummy/models/component-options-text';
let store, zoo;

module('unit - Polymorphism', function (hooks) {
Expand Down Expand Up @@ -83,4 +84,127 @@ module('unit - Polymorphism', function (hooks) {
assert.ok(second instanceof Elephant);
assert.equal(second.trunkLength, 4, "elephant's trunk length is correct");
});

test("fragment array's createFragment supports polymorphism with string typeKey", async function (assert) {
store.push({
data: {
type: 'zoo',
id: 1,
attributes: zoo,
},
});

const record = await store.find('zoo', 1);
const animals = record.animals;

const newLion = animals.createFragment({
$type: 'lion',
name: 'Alex',
hasManes: 'true',
});

assert.ok(newLion instanceof Animal, 'new lion is an animal');
assert.equal(newLion.name, 'Alex', "new animal's name is correct");
assert.ok(newLion instanceof Lion, 'new lion is a lion');
assert.ok(newLion.hasManes, 'lion has manes');
assert.strictEqual(newLion.zoo, record, 'set the fragment owner');

const newElephant = animals.createFragment({
$type: 'elephant',
name: 'Heffalump',
trunkLength: 7,
});

assert.ok(newElephant instanceof Animal, 'new elephant is an animal');
assert.equal(newElephant.name, 'Heffalump', "new animal's name is correct");
assert.ok(newElephant instanceof Elephant, 'new elephant is an elephant');
assert.equal(
newElephant.trunkLength,
7,
"elephant's trunk length is correct"
);
assert.strictEqual(newElephant.zoo, record, 'set the fragment owner');
});

test("fragment array's createFragment supports polymorphism with function typeKey", async function (assert) {
store.push({
data: {
type: 'component',
id: 1,
attributes: {
type: 'text',
optionsHistory: [],
},
},
});

const component = await store.find('component', 1);
const textOptions = component.optionsHistory.createFragment({
fontFamily: 'Verdana',
fontSize: 12,
});

assert.ok(
textOptions instanceof ComponentOptionsText,
'options is ComponentOptionsText'
);
assert.equal(
textOptions.fontFamily,
'Verdana',
'options has correct fontFamily attribute'
);
assert.equal(
textOptions.fontSize,
12,
'options has correct fontSize attribute'
);
assert.equal(
component.optionsHistory.length,
1,
'fragment object was added to fragment array'
);
});

test('createRecord supports polymorphic typeKey for fragment and fragment arrays', async function (assert) {
const zoo = store.createRecord('zoo', {
star: {
$type: 'lion',
name: 'Mittens',
hasManes: true,
},
animals: [
{
$type: 'lion',
name: 'Alex',
hasManes: false,
},
{
$type: 'elephant',
name: 'Heffalump',
trunkLength: 7,
},
],
});

const star = zoo.star;
assert.ok(star instanceof Lion, 'star is a lion');
assert.strictEqual(star.name, 'Mittens', 'star name is correct');
assert.strictEqual(star.hasManes, true, 'star has manes');
assert.strictEqual(star.zoo, zoo, 'star fragment owner is correct');

const animals = zoo.animals;
assert.strictEqual(animals.length, 2);

const lion = animals.firstObject;
assert.ok(lion instanceof Lion, 'first animal is a lion');
assert.strictEqual(lion.name, 'Alex', 'lion name is correct');
assert.false(lion.hasManes, 'lion does not have manes');
assert.strictEqual(lion.zoo, zoo, 'lion fragment owner is correct');

const elephant = animals.lastObject;
assert.ok(elephant instanceof Elephant, 'second animal is an elephant');
assert.strictEqual(elephant.name, 'Heffalump', 'elephant name is correct');
assert.strictEqual(elephant.trunkLength, 7, 'trunk length is correct');
assert.strictEqual(elephant.zoo, zoo, 'elephant fragment owner is correct');
});
});
Loading