Skip to content

Instantly share code, notes, and snippets.

@ramybenaroya
Last active May 25, 2017 15:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ramybenaroya/b116abf73fdf50a7bf706f2062c1efcd to your computer and use it in GitHub Desktop.
Save ramybenaroya/b116abf73fdf50a7bf706f2062c1efcd to your computer and use it in GitHub Desktop.
unloadRecord resets belongsTo
import DS from 'ember-data';
import Ember from 'ember';
var get = Ember.get;
var indexOf = Array.prototype.indexOf && function(array, item) {
return array.indexOf(item);
} || Ember.EnumerableUtils.indexOf;
var map = Array.prototype.map && function(array, cb, binding) {
return array.map(cb, binding);
} || Ember.EnumerableUtils.map;
var counter = 0;
/**
`DS.FixtureAdapter` is an adapter that loads records from memory.
It's primarily used for development and testing. You can also use
`DS.FixtureAdapter` while working on the API but is not ready to
integrate yet. It is a fully functioning adapter. All CRUD methods
are implemented. You can also implement query logic that a remote
system would do. It's possible to develop your entire application
with `DS.FixtureAdapter`.
For information on how to use the `FixtureAdapter` in your
application please see the [FixtureAdapter
guide](/guides/models/the-fixture-adapter/).
@class FixtureAdapter
@namespace DS
@extends DS.Adapter
*/
export default DS.Adapter.extend({
defaultSerializer: '-default',
// The fixture adapter does not support coalesceFindRequests
coalesceFindRequests: false,
/**
If `simulateRemoteResponse` is `true` the `FixtureAdapter` will
wait a number of milliseconds before resolving promises with the
fixture values. The wait time can be configured via the `latency`
property.
@property simulateRemoteResponse
@type {Boolean}
@default true
*/
simulateRemoteResponse: true,
/**
By default the `FixtureAdapter` will simulate a wait of the
`latency` milliseconds before resolving promises with the fixture
values. This behavior can be turned off via the
`simulateRemoteResponse` property.
@property latency
@type {Number}
@default 50
*/
latency: 50,
/**
Implement this method in order to provide data associated with a type
@method fixturesForType
@param {Subclass of DS.Model} typeClass
@return {Array}
*/
fixturesForType(typeClass) {
if (typeClass.FIXTURES) {
var fixtures = typeClass.FIXTURES;
return map(fixtures, (fixture) => {
var fixtureIdType = typeof fixture.id;
if (fixtureIdType !== 'number' && fixtureIdType !== 'string') {
throw new Error(`the id property must be defined as a number or string for fixture ${fixture}`);
}
fixture.id = fixture.id + '';
return fixture;
});
}
return null;
},
/**
Implement this method in order to query fixtures data
@method queryFixtures
@param {Array} fixture
@param {Object} query
@param {Subclass of DS.Model} typeClass
@return {Promise|Array}
*/
queryFixtures(/*fixtures, query, typeClass*/) {
Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.');
},
/**
@method updateFixtures
@param {Subclass of DS.Model} typeClass
@param {Array} fixture
*/
updateFixtures(typeClass, fixture) {
if (!typeClass.FIXTURES) {
typeClass.reopenClass({
FIXTURES: []
});
}
var fixtures = typeClass.FIXTURES;
this.deleteLoadedFixture(typeClass, fixture);
fixtures.push(fixture);
},
/**
Implement this method in order to provide json for CRUD methods
@method mockJSON
@param {DS.Store} store
@param {Subclass of DS.Model} typeClass
@param {DS.Snapshot} snapshot
*/
mockJSON(store, typeClass, snapshot) {
return store.serializerFor(snapshot.modelName).serialize(snapshot, { includeId: true });
},
/**
@method generateIdForRecord
@param {DS.Store} store
@param {DS.Model} record
@return {String} id
*/
generateIdForRecord(/*store*/) {
return `fixture-${counter++}`;
},
/**
@method find
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {String} id
@param {DS.Snapshot} snapshot
@return {Promise} promise
*/
findRecord(store, typeClass, id/*, snapshot*/) {
var fixtures = this.fixturesForType(typeClass);
var fixture;
Ember.assert(`Unable to find fixtures for model type ${typeClass.toString()}. If you're defining your fixtures using 'Model.FIXTURES = ...'', please change it to 'Model.reopenClass({ FIXTURES: ... })'.`, fixtures);
if (fixtures) {
fixture = Ember.A(fixtures).findBy('id', id);
}
if (fixture) {
return this.simulateRemoteCall(() => fixture);
}
},
/**
@method findMany
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {Array} ids
@param {Array} snapshots
@return {Promise} promise
*/
findMany(store, typeClass, ids/*, snapshots*/) {
var fixtures = this.fixturesForType(typeClass);
Ember.assert(`Unable to find fixtures for model type ${typeClass.toString()}`, fixtures);
if (fixtures) {
fixtures = fixtures.filter(item => indexOf(ids, item.id) !== -1);
}
if (fixtures) {
return this.simulateRemoteCall(() => fixtures);
}
},
/**
@private
@method findAll
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {String} sinceToken
@return {Promise} promise
*/
findAll(store, typeClass) {
var fixtures = this.fixturesForType(typeClass);
Ember.assert(`Unable to find fixtures for model type ${typeClass.toString()}`, fixtures);
return this.simulateRemoteCall(() => fixtures);
},
/**
@private
@method findQuery
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {Object} query
@param {DS.AdapterPopulatedRecordArray} recordArray
@return {Promise} promise
*/
query(store, typeClass, query/*, array*/) {
var fixtures = this.fixturesForType(typeClass);
Ember.assert(`Unable to find fixtures for model type ${typeClass.toString()}`, fixtures);
fixtures = this.queryFixtures(fixtures, query, typeClass);
if (fixtures) {
return this.simulateRemoteCall(() => fixtures);
}
},
/**
@method createRecord
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {DS.Snapshot} snapshot
@return {Promise} promise
*/
createRecord(store, typeClass, snapshot) {
var fixture = this.mockJSON(store, typeClass, snapshot);
this.updateFixtures(typeClass, fixture);
return this.simulateRemoteCall(() => fixture);
},
/**
@method updateRecord
@param {DS.Store} store
@param {subclass of DS.Model} type
@param {DS.Snapshot} snapshot
@return {Promise} promise
*/
updateRecord(store, typeClass, snapshot) {
var fixture = this.mockJSON(store, typeClass, snapshot);
this.updateFixtures(typeClass, fixture);
return this.simulateRemoteCall(() => fixture);
},
/**
@method deleteRecord
@param {DS.Store} store
@param {subclass of DS.Model} typeClass
@param {DS.Snapshot} snapshot
@return {Promise} promise
*/
deleteRecord(store, typeClass, snapshot) {
this.deleteLoadedFixture(typeClass, snapshot);
return this.simulateRemoteCall(() => null);
},
/*
@method deleteLoadedFixture
@private
@param typeClass
@param snapshot
*/
deleteLoadedFixture(typeClass, snapshot) {
var existingFixture = this.findExistingFixture(typeClass, snapshot);
if (existingFixture) {
var index = indexOf(typeClass.FIXTURES, existingFixture);
typeClass.FIXTURES.splice(index, 1);
return true;
}
},
/*
@method findExistingFixture
@private
@param typeClass
@param snapshot
*/
findExistingFixture(typeClass, snapshot) {
var fixtures = this.fixturesForType(typeClass);
var id = snapshot.id;
return this.findFixtureById(fixtures, id);
},
/*
@method findFixtureById
@private
@param fixtures
@param id
*/
findFixtureById(fixtures, id) {
return Ember.A(fixtures).find((r) => '' + get(r, 'id') === '' + id);
},
/*
@method simulateRemoteCall
@private
@param callback
@param context
*/
simulateRemoteCall(callback, context) {
var adapter = this;
return new Ember.RSVP.Promise(function(resolve) {
var value = Ember.copy(callback.call(context), true);
if (get(adapter, 'simulateRemoteResponse')) {
// Schedule with setTimeout
Ember.run.later(null, resolve, value, get(adapter, 'latency'));
} else {
// Asynchronous, but at the of the runloop with zero latency
resolve(value);
}
}, 'DS: FixtureAdapter#simulateRemoteCall');
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
actions: {
unloadBooks(){
this.store.unloadAll('book');
}
}
});
import Model from "ember-data/model";
import attr from "ember-data/attr";
import { belongsTo, hasMany } from "ember-data/relationships";
const Book = Model.extend({
name: attr('string')
});
Book.reopenClass({
FIXTURES: [{
id: 'book1',
name: 'The Holy Bible'
}]
})
export default Book;
import Model from "ember-data/model";
import attr from "ember-data/attr";
import { belongsTo, hasMany } from "ember-data/relationships";
const Person = Model.extend({
name: attr('string'),
book: belongsTo('book', { async: true})
});
Person.reopenClass({
FIXTURES: [{
id: 'person1',
name: 'John Smith',
book: 'book1'
}]
})
export default Person;
import Ember from 'ember';
import config from './config/environment';
const Router = Ember.Router.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('book', { path: 'books/:book_id'})
this.route('person', { path: 'persons/:person_id'})
});
export default Router;
import Ember from 'ember';
import Promise from 'rsvp';
export default Ember.Route.extend({
model(params){
const person = this.store.findRecord('person', 'person1');
const book = person.then(p => p.get('book'));
const isBookNull = book.then(b => b === null)
return Promise.hash({
person,
book,
isBookNull
})
}
});
<h1>Unload Records sets belongsTo to null</h1>
Step 1
{{#link-to 'person' 'person1'}}
Go to person1
{{/link-to}} (loading person1 model and its book with Promise.hash)
<br>
Step 2
{{#link-to 'index'}}
Back to index
{{/link-to}}
<br>
Step 3
<button onclick={{action 'unloadBooks'}}>store.unloadAll('book')</button>
<br>
Step 4
{{#link-to 'person' 'person1'}}
Go back to person1
{{/link-to}} (you can see that the book name is missing)
<h3>Outlet: </h3>
<div style='border:1px solid black; padding: 10px;'>
{{outlet}}
</div>
Name: {{model.name}}
Name: {{model.person.name}}
<br>
Book: {{model.book.name}}
<br>
book === null : {{model.isBookNull}}
import Ember from 'ember';
export default function destroyApp(application) {
Ember.run(application, 'destroy');
}
import Resolver from '../../resolver';
import config from '../../config/environment';
const resolver = Resolver.create();
resolver.namespace = {
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix
};
export default resolver;
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
const { run } = Ember;
const assign = Ember.assign || Ember.merge;
export default function startApp(attrs) {
let application;
let attributes = assign({rootElement: "#test-root"}, config.APP);
attributes = assign(attributes, attrs); // use defaults, but you can override;
run(() => {
application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();
});
return application;
}
import resolver from './helpers/resolver';
import {
setResolver
} from 'ember-qunit';
setResolver(resolver);
{
"version": "0.12.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.12.0",
"ember-template-compiler": "2.12.0",
"ember-testing": "2.12.0"
},
"addons": {
"ember-data": "2.12.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment