kurko / ember-sync Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
When embersync.save() is called a request is sent to online store. But when page is refreshed another request is sent and generate a duplicate on online store. Embersync.save() is not re-called after page refresh. It seems that the request remains in queue and it's re-executed.
There is a known solution to this problem?
Calling processNextJob
when there's no jobs pending seems to make little sense. Queue could run on each enqueue
, and start the retry intervals only on job error. If app goes offline for hours though, it may make sense to offer an API to retry it manually, or on a window.addEventListener('online', callback)
event.
Move this to https://github.com/ud-sync organization.
When updating a record, the older version seems to persist in localStore. It happens like this:
model
hook fires but the enqueued update of step 2 didn't yet arrive to the server.Now if I retrigger the index model hook, or reload, the update is of course there. Should localStore be the source of truth, and push changes always but not pull? Should online find not fire if there's enqueued operations? I'm not sure how to deal with this issue yet.
Thank you for your time.
What Ember Sync is trying to achieve which haven't been achieved by CouchDB?
Can anyone shed some light on this?
I updated ember-cli to 1.13.8 and the following
Ember : 1.13.7
Ember Data : 1.13.8
jQuery : 1.11.3
Now I am getting the following error:
Error: Assertion Failed: Passing classes to store methods has been removed. Please pass a dasherized string instead of my-new-app@model:ember-sync-queue-model:
Any idea how to solve the problem?
Regards,
Nick
I am trying to hook up a forked local version of ember-sync to a sample app.
I have a ember-sync submodule under vendor and a brocfile that contains an import of the ember-sync's dist js file.
Here is the project -> https://github.com/Genkilabs/ember-cli-example/tree/develop
Any idea how we can get this hooked up to dev on locally?
This lib based on ember-sync idea.
Is this project still alive? Is it maintained?
Small piece of code for this, use
this.emberSync.findOfflineFirst('posts');
or
this.emberSync.findOfflineFirst('post','27');
to get a promise filled with offline store results if any (without fetching the server), otherwise online store results fetched from the server. This can be useful if we want to use localStorage as a cache of user's own data, and reduce the number of requests to the server for records which can only be modified by the user (while still being able to send changes to the server):
import EmberSync from 'ember-sync';
import StoreInitMixin from "ember-sync/store-initialization-mixin";
import Persistence from "ember-sync/persistence";
import Query from 'ember-sync/query';
import Ember from 'ember';
var alreadyRun=false;
export default {
name: 'override-ember-sync',
before: 'store',
initialize: function() {
if (alreadyRun) {
return;
} else {
alreadyRun=true;
}
EmberSync.reopen(StoreInitMixin,{
findOfflineFirst: function(type, query) {
var _this = this;
var syncQuery = Query.create({
onlineStore: this.onlineStore,
offlineStore: this.offlineStore,
onError: this.get('onError'),
onRecordAdded: function(record) {
_this.onRecordAdded(record, type);
}
});
return syncQuery.findOfflineFirst(type, query);
},
});
Query.reopen({
findOfflineFirst: function(type, query) {
var _this=this;
var offlineSearch = Ember.isNone(query) ? _this.offlineStore.findAll(type) : _this.offlineStore.find(type, query);
return new Ember.RSVP.Promise(function(resolve, reject) {
offlineSearch.then(function(record) {
if (!record.toArray().length) {
_this.findOnlineOnly(type, query).then(function(record){resolve(record);},
function(error){reject(error);});
}
else {
//see https://github.com/kurko/ember-localstorage-adapter/issues/113
record.forEach(function(rec){
if (record.get('id')) {
_this.onRecordAdded(record);
}
});
resolve(record);
}
},
function(error) {
_this.findOnlineOnly(type, query).then(function(record){resolve(record);},
function(error){reject(error);});
});
});
},
findOnlineOnly: function(type, query){
var _this=this;
var onlineSearch = Ember.isNone(query) ? _this.onlineStore.findAll(type) : _this.onlineStore.find(type, query);
return new Ember.RSVP.Promise(function(resolve, reject) {
onlineSearch.then(function(record) {
if (!record.toArray().length) {reject(record);}
else{
record.forEach(function(rec){
var id = rec.get('id'),
persistenceState = _this.offlineStore.find(type, id);
var persistRecordOffline = function(onlineRecord) {
var persistence = Persistence.create({
onlineStore: _this.onlineStore,
offlineStore: _this.offlineStore,
});
persistence.persistRecordOffline(type,rec);
};
persistenceState.then(persistRecordOffline, persistRecordOffline);
_this.onRecordAdded(rec);
});
resolve(record);
}
},function(error){
reject(error);
});
});
},
});
},
}
I am trying to get ember-localstorage-adapter working with ember-sync, ember 1.10, ember-cli 0.2.0. I upgraded to ember-data 1.0.0-beta.16. Anytime I try to access this.emberSync.find('modelName', 1)
, I encounter this error.
TypeError: Cannot read property 'lookup' of undefined
at ember$data$lib$system$serializer$$default.extend.transformFor (http://localhost:5000/assets/vendor.js:68222:39)
at applyTransform (http://localhost:5000/assets/vendor.js:67257:32)
at http://localhost:5000/assets/vendor.js:73911:20
at Map.forEach.cb (http://localhost:5000/assets/vendor.js:23590:13)
at OrderedSet.forEach (http://localhost:5000/assets/vendor.js:23381:13)
at Map.forEach (http://localhost:5000/assets/vendor.js:23594:19)
at Function.ember$data$lib$system$model$model$$default.reopenClass.eachTransformedAttribute (http://localhost:5000/assets/vendor.js:73910:84)
at ember$data$lib$system$serializer$$default.extend.applyTransforms (http://localhost:5000/assets/vendor.js:67254:14)
at ember$data$lib$serializers$json$serializer$$default.extend.normalize (http://localhost:5000/assets/vendor.js:68401:14)
at superFunction [as _super] (http://localhost:5000/assets/vendor.js:23784:22)
It looks like the application adapter is not available. Is this likely a problem with my configuration of ember-localstorage-adapter or ember-sync?
While I had to use a different github repo to install this, I managed to get it installed and setup.
However when I create a record and save it, nothing is being transmitted to the server.
Here is my online-store.js initializer:
import DS from "ember-data";
import ENV from "recruiter/config/environment";
var CustomOnlineAdapter = DS.RESTAdapter.extend({
host: ENV.apiHost
});
var OnlineStore = DS.Store.extend({
adapterFor: function(type){
return this.container.lookup('adapter:_custom_store');
}
});
export function initialize(container, application ) {
container.register('store:online', OnlineStore);
container.register('adapter:_custom_store', CustomOnlineAdapter);
application.inject('route', 'onlineStore', 'store:online');
application.inject('controller','onlineStore', 'store:online');
}
export default {
name: 'onlineStore',
initialize: initialize
};
Here is my application route:
import Ember from 'ember';
export default Ember.Route.extend({
init: function(){
this._super();
this.emberSync.set('offlineStore', this.store);
this.emberSync.set('onlineStore', this.onlineStore);
this.emberSync.synchronizeOnline();
},
model: function(){
return this.emberSync.find('event');
},
setupController: function(controller, model){
}
});
Now, in the application controller, doing:
this.emberSync.createRecord('event', { title: 'foo' }).save();
Only stores it locally, nothing is transmitted online.
I'm using your localstorage ember-cli addon for the local storage piece. And I'm using the built-in Ember-Data RESTAdapter for the online adapter.
this.emberSync.find('farm', 1).then(function(farm) {
return farm.emberSync === undefined;
});
That code is true. This means that if I find a model through emberSync
I can't save it through emberSync
(and persist the changes in both online and offline form.
So fetching data from firebase works perfectly (with this.emberSync.find('user', '9nj32')), but trying to save to firebase (via EmberFire) doesn't work and the worst part it doesn't throw any errors.
For example:
export default Ember.Route.extend({
model: function () {
return this.emberSync.find('user', '2oj9m').then(function(record){
return user = record;
});
},
actions: {
save: function () {
// this.modelFor('index').save();
// debugger;
// user.get('name')
user.save();
// return user.emberSync.save();
}
}
});
with a template:
{{input value=name}}
<button {{action "save"}}>Save</button>
Any ideas? How would you save to firebase?
P.S. I am also using ember localForage adapter
If you install the npm package with the latest ember-cli it throws an error that loader.js is a file as well as a folder or something.
Installing via bower and setting things up manually works.
This is the standard that PostgreSQL follows. See:
It's also the one used in Backbone.dualStorage, which allows client and server side id generation, with trivial syncing: https://github.com/nilbus/Backbone.dualStorage/blob/master/backbone.dualstorage.js#L82-L96
Sending ids to a Postgres API now raises: ERROR: invalid input syntax for uuid: "ducg0"
.
If I define my DS.hasMany('child')
as DS.attr('array')
(I've got a trivial array Transform
), then in ember-sync I can see the array of child ids in the parent object.
When I leave the hasMany
call, then the attribute gets undefined
. Debugging lib/ember-sync/query.js
:
onlinePromise.then(function(results) {
// ...
results.forEach(function(onlineResult) {
onlineResult.get('childs') // => undefined
My onlineStore
is the same as my ex-RESTAdapter, only moved to plugin ember-sync
in the middle. API has no changes. Why could this be happening? Where should I continue digging into this issue?
I have the following code to create a new model:
testModel = this.emberSync.createRecord('test', {
email: this.get('email'),
name: this.get('name'),
});
testModel.emberSync.save();
This works fine and the model gets saved to the server.
At a later time (after the save has been successfully completed) the user enters something and the model gets updated as follows:
testModel.set('name', 'anothername');
testModel.set('otherAttribute', 'otherValue');
console.log(testModel.get('isDirty')) //true
testModel.emberSync.save();
Ember Inspector shows the new name, otherAttribute and isDirty is true. But no request gets sent to the server.
Removing ember-sync and it works fine.
Any idea what the issue could be?
Edit: Storing offline seems to work though.
Hi, getting into ember-sync and this is a great work thanks for sharing.
Different points below more or less linked, so I prefer to not open several github issues:
npmjs.com repo not up to date? It may be obvious for many people to check the version when they do 'npm install ember-sync' but I didn't and I spent some hours debugging the same things you have dealt with these last months.. (I am discovering npm, bower etc. so this may be a very naive point).
Doc: my understanding is that if you don't create / save or find a post Record through emberAsync, you will not be able to call post.emberAsync on it later on:
=> this may be clearer in the docs?
When finding a record through this.emberSync.findQuery, the result's relationships don't have the method 'emberSync', so not possible to save them to both stores etc. (in my case, I change one relationship in a form but can not properly save it after).
I think I see a trick to solve that (using this.emberSync.onRecordAdded(inst,inst.get('constructor.typeKey'))), but at the very least this is not in the docs.
=> It would be nice to see emberSyncing relationships possible in a future implementation, probably this would look something like "post.async.get('comment')"?
It would be nice to be able to not fetch from the server if not needed, for example if we know that the records, if they exist, are more recent locally (the user is the author of these records).
Admittedly we could use the offline store directly for this, but we could want to post the records both online and offline later on, after having fetched them and if not initialized properly with emberSync this is not possible.
=> So overall, ideally here would be more options for fetching records, like: 1) fetch only if they don't exist in the offline store [ 2) fetch only if they are more recent on the server (sending an updated_at in the request) etc. ]. 1) would really help in using emberSync for caching data locally.
=> A dirty solution in #33
Small things:
Many thanks for the excellent work,
L
When using this.emberSync.find('post'), after fetching data from the server, the relationships are not pushed to the offline Store, only to the online Store: in ActiveModel serializer, extractArray pushes the relationships in the Store, while returning the primary Array. emberSync's find query uses the results returned by onlineSearch = this.onlineStore.find(type, query), which only returns the primary array.
To have the relationships pushed to the offlineStore, I had to override the ActiveModelSerializer:
export default DS.ActiveModelSerializer.extend({
//(..)
extractArray: function(store, primaryType, rawPayload) {
//(..)
if (isPrimary) {
primaryArray = normalizedArray;
} else {
var records=this.container.lookup('store:main').pushMany(typeName, Ember.copy(normalizedArray,true));//added
records.forEach(function(record){//added
record.save();//added
});//added
store.pushMany(typeName, normalizedArray);
}
}
return primaryArray;
},
extractSingle: function(store, primaryType, rawPayload, recordId) {
//(..)
if (isFirstCreatedRecord || isUpdatedRecord) {
primaryRecord = hash;
} else {
var record=this.container.lookup('store:main').push(typeName, Ember.copy(hash,true));//added
record.save();//added
store.push(typeName, hash);
}
}, this);
}
return primaryRecord;
},
I had the issue described in http://stackoverflow.com/questions/21724343/no-container-in-jsonserializer on my app, using an online store initializer as described in the docs. Solution was to get rid of declaring the serializer in CustomOnlineAdapter definition:
var CustomOnlineSerializer = ActiveModelSerializer.extend();
var CustomOnlineAdapter = ActiveModelAdapter.extend({
//serializer: CustomOnlineSerializer.create(), http://stackoverflow.com/questions/21724343/no-container-in-jsonserializer
});
(with ember-cli & Ember 1.11)
_serializeAssociations: function() {
(..)
var pushToCollection = function(snapshot) {
if (typeof(snapshot)==='undefined') {return true;}//added
(..)
},
Say a record exists on both online and offline store.
Now, somebody deletes it from the online store.
The next time I call emberSync.find(), will it be deleted from the offline store?
Just want to know if you can confirm the behavior (currently unable to test).
I looked over the code and this does not seem to be the case. Thanks!
I am using localForage ember adapter, EmberFire, ember-sync. This is a continuation from #22
Also, http://stackoverflow.com/questions/26289012/embersync-find-works-but-embersync-save-doesnt-why
Not sure if you are still working on this, and perhaps it's version issues, but just in case...
ember-data: 1.0.0-beta.17
ember-sync: 0.2.3
I'm using LSAdapter for offline and ActiveModelAdapter for online. I also have a few model specific serializers, so I defined two sets of serializers inherited from both LSSerializer and ActiveModelSerializer, and have them wired up to offlineStore and onlineStore respectively.
It seems the problem is, when the app starts, it calls emberSync.find() to find some records. Since it's the first time, nothing is in localStorage. It then tries to write to offlineStore with the record found online. However, for some reason the record.get('isNew') is always false, forcing the "operation" in DS.store.flushPendingSave() to be 'updateRecord' instead of 'createRecord'. Then it tries to update but couldn't find the record and ends up throwing exception.
Any tips on how to fix this? Thank you.
client master % npm install --save ember-sync
npm WARN package.json [email protected] No repository field.
npm http GET https://registry.npmjs.org/ember-sync
npm http 304 https://registry.npmjs.org/ember-sync
> [email protected] postinstall /Users/tute/Sites/agromentor/client/node_modules/ember-sync
> ./node_modules/bower/bin/bower install
sh: ./node_modules/bower/bin/bower: No such file or directory
npm ERR! [email protected] postinstall: `./node_modules/bower/bin/bower install`
npm ERR! Exit status 127
npm ERR!
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is most likely a problem with the ember-sync package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! ./node_modules/bower/bin/bower install
npm ERR! You can get their info via:
npm ERR! npm owner ls ember-sync
npm ERR! There is likely additional logging output above.
npm ERR! System Darwin 13.2.0
npm ERR! command "/usr/local/Cellar/node/0.10.26/bin/node" "/usr/local/bin/npm" "install" "--save" "ember-sync"
npm ERR! cwd /Users/tute/Sites/agromentor/client
npm ERR! node -v v0.10.26
npm ERR! npm -v 1.4.3
npm ERR! code ELIFECYCLE
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR! /Users/tute/Sites/agromentor/client/npm-debug.log
npm ERR! not ok code 0
Thank you for your work!
a post and many comments exist and are stored offline.
When refreshing the page, the offline models are created, and no online models exist.
If refreshing only the post from the server (and returning the list of ids for the comments), the online store will create empty records for the comments, as it is not aware of any existing comments yet. These empty comments are then pushed to the offline server, overriding the old, correct, comments.
A solution: don't push to collection associations when the corresponding models are empty:
Record.reopen({
_serializeAssociations: function() {
var serializedCollection = [],
_this = this,
snapshot = this._snapshot();
snapshot.eachRelationship(function(name, relationship) {
var hasManyRecords = null;
var pushToCollection = function(snapshot) {
if (typeof(snapshot)==='undefined') {return true;}
if (snapshot.get('isEmpty')) {
//added: don't replace offline record..
return true;
}
//(..)
});
},
});
I'm not sure in which cases ember doesn't returns promises for relations (it seems to be the standard behaviour with ember-data, even for async=false relationship), but in case it helps this is how I changed your code to work with promises (haven't checked if hasMany relationships work yet):
import RecordForSynchronization from 'ember-sync/record-for-synchronization';
import Ember from 'ember';
var alreadyRun=false;
export default {
name: 'override-ember-sync',
initialize: function() {
if (alreadyRun) {
return;
} else {
alreadyRun=true;
}
RecordForSynchronization.reopen({
toEmberData: function() {
var _this = this,
record;
return new Ember.RSVP.Promise(function(resolve, reject) {
record = _this.createRecordInStore();
_this.setRelationships(record).then(function(rec){
resolve(rec);
});
});
},
setRelationships: function(pendingRecord) {
var _this = this,
offlineRecord = this.get('offlineRecord'),
type = this.get('jobRecord.jobRecordType');
/**
* We need to loop relationships. If no record was
* passed in, we need to create a new one, fake, so we know about the
* relationships.
*/
if (!offlineRecord) {
offlineRecord = this.onlineStore.push(type, {id: 1});
}
var promises=[];
offlineRecord.eachRelationship(function(name, descriptor) {
var key = descriptor.key,
kind = descriptor.kind,
type = descriptor.type,
relation, onlineRelation;
/**
* TODO - implement for when `.get(relation)` returns a Promise.
*/
promises.push(offlineRecord.get(name).then(function(relation){
/**
* We need to attach relationships to the main record. If the
* relationships don't exist anymore offline, we need to generate a new
* one, fake, with the same ID, just to send to the server.
*/
if (kind == "belongsTo") {
var relationId = _this.get('jobRecord.serialized')[name];
if (relationId && !relation) {
relation = _this.onlineStore.push(type, {id: relationId});
}
if (relation) {
onlineRelation = _this.generateRelationForRecord(type, relation);
pendingRecord.set(key, onlineRelation);
}
} else if (kind == "hasMany") {
relation.forEach(function(relation) {
onlineRelation = _this.generateRelationForRecord(type, relation);
pendingRecord.get(name).pushObject(onlineRelation);
});
}
return pendingRecord;
}));
});
return Ember.RSVP.all(promises).then(function(res){
return pendingRecord;
});
},
});
},
}
Following code performs a DELETE
HTTP request ad eternum:
this.emberSync.deleteRecord('user', record);
record.emberSync.save();
This is because upon successful (first) DELETE
call the queue is not cleaned up, and the operations is retried every 5 seconds.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.