From 529a5de7925df714bb53d37de8ad167418bd3a68 Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Wed, 2 Sep 2015 10:27:39 +0200 Subject: [PATCH 1/8] db can now be switched dynamically on the adapter, allowing to have a dynamic adapter --- addon/adapters/pouch.js | 93 +++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index 9ee19cc..f13e78a 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -26,13 +26,19 @@ export default DS.RESTAdapter.extend({ shouldReloadRecord: function () { return false; }, shouldBackgroundReloadRecord: function () { return false; }, - _startChangesToStoreListener: on('init', function () { - this.changes = this.get('db').changes({ - since: 'now', - live: true, - returnDocs: false - }).on('change', bind(this, 'onChange')); + _hookObservers : on('init', function() { + this.addObserver('db', this, this._startChangesToStoreListener) }), + _startChangesToStoreListener: function () { + var db = this.get('db'); + if (db) { + this.changes = this.get('db').changes({ + since: 'now', + live: true, + returnDocs: false + }).on('change', bind(this, 'onChange')); + } + }, onChange: function (change) { // If relational_pouch isn't initialized yet, there can't be any records @@ -81,7 +87,8 @@ export default DS.RESTAdapter.extend({ _init: function (store, type) { var self = this, recordTypeName = this.getRecordTypeName(type); - if (!this.get('db') || typeof this.get('db') !== 'object') { + var db = this.get('db'); + if (!db || typeof db !== 'object') { throw new Error('Please set the `db` property on the adapter.'); } @@ -97,49 +104,55 @@ export default DS.RESTAdapter.extend({ var plural = pluralize(recordTypeName); // check that we haven't already registered this model + var isSchemaNew = true; for (var i = 0, len = this._schema.length; i < len; i++) { var currentSchemaDef = this._schema[i]; if (currentSchemaDef.singular === singular) { - return; + isSchemaNew = false; + break; } } - var schemaDef = { - singular: singular, - plural: plural - }; - - if (type.documentType) { - schemaDef['documentType'] = type.documentType; - } - // else it's new, so update - this._schema.push(schemaDef); - - // check all the subtypes - // We check the type of `rel.type`because with ember-data beta 19 - // `rel.type` switched from DS.Model to string - type.eachRelationship(function (_, rel) { - if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') { - // TODO: support inverse as well - return; // skip + if (isSchemaNew) { + var schemaDef = { + singular: singular, + plural: plural + }; + + if (type.documentType) { + schemaDef['documentType'] = type.documentType; } - var relDef = {}, - relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type); - if (relModel) { - relDef[rel.kind] = { - type: self.getRecordTypeName(relModel), - options: rel.options - }; - if (!schemaDef.relations) { - schemaDef.relations = {}; + + this._schema.push(schemaDef); + // check all the subtypes + // We check the type of `rel.type`because with ember-data beta 19 + // `rel.type` switched from DS.Model to string + type.eachRelationship(function (_, rel) { + if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') { + // TODO: support inverse as well + return; // skip } - schemaDef.relations[rel.key] = relDef; - self._init(store, relModel); - } - }); + var relDef = {}, + relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type); + if (relModel) { + relDef[rel.kind] = { + type: self.getRecordTypeName(relModel), + options: rel.options + }; + if (!schemaDef.relations) { + schemaDef.relations = {}; + } + schemaDef.relations[rel.key] = relDef; + self._init(store, relModel); + } + }); + } + + if (!db.rel) { + db.setSchema(this._schema); + } - this.get('db').setSchema(this._schema); }, _recordToData: function (store, type, record) { From 26b256786664527c8b536df397df3b8e76021b5d Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Wed, 2 Sep 2015 11:25:47 +0200 Subject: [PATCH 2/8] small cosmetic fix --- addon/adapters/pouch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index f13e78a..0751233 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -32,7 +32,7 @@ export default DS.RESTAdapter.extend({ _startChangesToStoreListener: function () { var db = this.get('db'); if (db) { - this.changes = this.get('db').changes({ + this.changes = db.changes({ since: 'now', live: true, returnDocs: false From 71201e11a2b3dd4709fed6aaaa7efde1ccd18501 Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Wed, 2 Sep 2015 11:48:00 +0200 Subject: [PATCH 3/8] build fix --- addon/adapters/pouch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index 0751233..31f2501 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -27,7 +27,7 @@ export default DS.RESTAdapter.extend({ shouldBackgroundReloadRecord: function () { return false; }, _hookObservers : on('init', function() { - this.addObserver('db', this, this._startChangesToStoreListener) + this.addObserver('db', this, this._startChangesToStoreListener); }), _startChangesToStoreListener: function () { var db = this.get('db'); From 1f7441c64b2db64a109773dc2f415adc89d04c2d Mon Sep 17 00:00:00 2001 From: olivier Date: Sat, 5 Sep 2015 10:06:17 +0200 Subject: [PATCH 4/8] changing database is now done through a call to changeDb --- README.md | 25 +++++++++++ addon/adapters/pouch.js | 95 ++++++++++++++++++++++------------------- 2 files changed, 76 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 496abb1..7fabe0d 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,31 @@ The value for `documentType` is the camelCase version of the primary model name. For best results, only create/update records using the full model definition. Treat the others as read-only. +## Multiple databases for the same model + +In some cases it might diserable (security related, where you want a given user to only have some informations stored on his computer) to have multiple databases for the same model of data. + +`Ember-Pouch` allows you to dynamically change the database a model is using by calling the function `changeDb` on the adapter. + +```javascript +function changeProjectDatabase(dbName, dbUser, dbPassword) { + // CouchDB is serving at http://localhost:5455 + let remote = new PouchDB('http://localhost:5455/' + dbName); + // here we are using pouchdb-authentication for credential supports + remote.login( dbUser, dbPassword).then( + function (user) { + let db = new PouchDB(dbName) + db.sync(remote, {live:true, retry:true}) + // grab the adapter, it can be any ember-pouch adapter. + let adapter = this.store.adapterFor('project'); + // this is where we told the adapter to change the current database. + adapter.changeDb(db); + } + ) +} +``` + + ## Installation * `git clone` this repository diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index 31f2501..90e9c7d 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -25,9 +25,8 @@ export default DS.RESTAdapter.extend({ // reloading redundant. shouldReloadRecord: function () { return false; }, shouldBackgroundReloadRecord: function () { return false; }, - - _hookObservers : on('init', function() { - this.addObserver('db', this, this._startChangesToStoreListener); + _onInit : on('init', function() { + this._startChangesToStoreListener(); }), _startChangesToStoreListener: function () { var db = this.get('db'); @@ -39,7 +38,22 @@ export default DS.RESTAdapter.extend({ }).on('change', bind(this, 'onChange')); } }, + changeDb: function(db) { + if (this.changes) { + this.changes.cancel(); + } + + var store = this.container.lookup('store:main'); + var schema = this._schema || []; + + for (var i = 0, len = schema.length; i < len; i++) { + store.unloadAll(schema[i].singular); + } + this._schema = null; + this.set('db', db); + this._startChangesToStoreListener(); + }, onChange: function (change) { // If relational_pouch isn't initialized yet, there can't be any records // in the store to update. @@ -87,8 +101,7 @@ export default DS.RESTAdapter.extend({ _init: function (store, type) { var self = this, recordTypeName = this.getRecordTypeName(type); - var db = this.get('db'); - if (!db || typeof db !== 'object') { + if (!this.get('db') || typeof this.get('db') !== 'object') { throw new Error('Please set the `db` property on the adapter.'); } @@ -104,55 +117,49 @@ export default DS.RESTAdapter.extend({ var plural = pluralize(recordTypeName); // check that we haven't already registered this model - var isSchemaNew = true; for (var i = 0, len = this._schema.length; i < len; i++) { var currentSchemaDef = this._schema[i]; if (currentSchemaDef.singular === singular) { - isSchemaNew = false; - break; + return; } } - // else it's new, so update - if (isSchemaNew) { - var schemaDef = { - singular: singular, - plural: plural - }; - - if (type.documentType) { - schemaDef['documentType'] = type.documentType; - } + var schemaDef = { + singular: singular, + plural: plural + }; - this._schema.push(schemaDef); - // check all the subtypes - // We check the type of `rel.type`because with ember-data beta 19 - // `rel.type` switched from DS.Model to string - type.eachRelationship(function (_, rel) { - if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') { - // TODO: support inverse as well - return; // skip - } - var relDef = {}, - relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type); - if (relModel) { - relDef[rel.kind] = { - type: self.getRecordTypeName(relModel), - options: rel.options - }; - if (!schemaDef.relations) { - schemaDef.relations = {}; - } - schemaDef.relations[rel.key] = relDef; - self._init(store, relModel); - } - }); + if (type.documentType) { + schemaDef['documentType'] = type.documentType; } - if (!db.rel) { - db.setSchema(this._schema); - } + // else it's new, so update + this._schema.push(schemaDef); + + // check all the subtypes + // We check the type of `rel.type`because with ember-data beta 19 + // `rel.type` switched from DS.Model to string + type.eachRelationship(function (_, rel) { + if (rel.kind !== 'belongsTo' && rel.kind !== 'hasMany') { + // TODO: support inverse as well + return; // skip + } + var relDef = {}, + relModel = (typeof rel.type === 'string' ? store.modelFor(rel.type) : rel.type); + if (relModel) { + relDef[rel.kind] = { + type: self.getRecordTypeName(relModel), + options: rel.options + }; + if (!schemaDef.relations) { + schemaDef.relations = {}; + } + schemaDef.relations[rel.key] = relDef; + self._init(store, relModel); + } + }); + this.get('db').setSchema(this._schema); }, _recordToData: function (store, type, record) { From 54254ce5e1e866656a2b08dd47bdc242f669f424 Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Tue, 13 Oct 2015 10:54:47 +0200 Subject: [PATCH 5/8] fixing deprecation warnings --- addon/adapters/pouch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index 90e9c7d..7cd5d99 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -43,7 +43,7 @@ export default DS.RESTAdapter.extend({ this.changes.cancel(); } - var store = this.container.lookup('store:main'); + var store = this.container.lookup('service:store'); var schema = this._schema || []; for (var i = 0, len = schema.length; i < len; i++) { @@ -73,12 +73,12 @@ export default DS.RESTAdapter.extend({ return; } - var recordInStore = store.getById(obj.type, obj.id); + var recordInStore = store.peekRecord(obj.type, obj.id); if (!recordInStore) { // The record hasn't been loaded into the store; no need to reload its data. return; } - if (!recordInStore.get('isLoaded') || recordInStore.get('isDirty')) { + if (!recordInStore.get('isLoaded') || recordInStore.get('hasDirtyAttributes')) { // The record either hasn't loaded yet or has unpersisted local changes. // In either case, we don't want to refresh it in the store // (and for some substates, attempting to do so will result in an error). From e270115da15ea23f5e8f37071ceba1c04f44feae Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Fri, 20 Nov 2015 13:00:00 +0100 Subject: [PATCH 6/8] fixes livesync for ember data 2.x --- addon/adapters/pouch.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index 7cd5d99..e199f69 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -63,7 +63,7 @@ export default DS.RESTAdapter.extend({ // skip changes for non-relational_pouch docs. E.g., design docs. if (!obj.type || !obj.id || obj.type === '') { return; } - var store = this.container.lookup('store:main'); + var store = this.container.lookup('service:store'); try { store.modelFor(obj.type); @@ -78,7 +78,9 @@ export default DS.RESTAdapter.extend({ // The record hasn't been loaded into the store; no need to reload its data. return; } - if (!recordInStore.get('isLoaded') || recordInStore.get('hasDirtyAttributes')) { + // removed || recordInStore.get('hasDirtyAttributes') because it does not track + // relationships correctly. + if (!recordInStore.get('isLoaded')) { // The record either hasn't loaded yet or has unpersisted local changes. // In either case, we don't want to refresh it in the store // (and for some substates, attempting to do so will result in an error). From 962c0cfeb348e3b34944938b97ee3b4ecd4ed0fb Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Fri, 20 Nov 2015 13:03:30 +0100 Subject: [PATCH 7/8] reverted stupid mistake --- addon/adapters/pouch.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index e199f69..ac94922 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -78,9 +78,7 @@ export default DS.RESTAdapter.extend({ // The record hasn't been loaded into the store; no need to reload its data. return; } - // removed || recordInStore.get('hasDirtyAttributes') because it does not track - // relationships correctly. - if (!recordInStore.get('isLoaded')) { + if (!recordInStore.get('isLoaded') || recordInStore.get('hasDirtyAttributes')) { // The record either hasn't loaded yet or has unpersisted local changes. // In either case, we don't want to refresh it in the store // (and for some substates, attempting to do so will result in an error). From 0b5e8b8e3b39be2a92599b9b6b813c715e8220c8 Mon Sep 17 00:00:00 2001 From: Olivier Chatry Date: Tue, 15 Dec 2015 22:39:13 +0100 Subject: [PATCH 8/8] use this.store instead of container.lookup --- addon/adapters/pouch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/adapters/pouch.js b/addon/adapters/pouch.js index d8a2016..0712346 100644 --- a/addon/adapters/pouch.js +++ b/addon/adapters/pouch.js @@ -43,7 +43,7 @@ export default DS.RESTAdapter.extend({ this.changes.cancel(); } - var store = this.container.lookup('service:store'); + var store = this.store; var schema = this._schema || []; for (var i = 0, len = schema.length; i < len; i++) {