Giter Site home page Giter Site logo

ember-sync's People

Contributors

alexferreira avatar jakecraige avatar kurko avatar tute avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ember-sync's Issues

small question about offline syncing

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!

EmberSync make another request when page is refreshed

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?

TODO: implement for when '.get(relation)' returns a Promise

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;
        });
      },
    });
  },
}

Updating an exisitng model doesn't work

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.

Don't override the offlineStore records with empty records created on the online store due to id-only relationships fetched

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;
            }
           //(..)
        });
      },
    });

Should locally generated UUIDs follow RFC4122?

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".

Setup per instructions, no online syncing happening

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.

Should localStore be the source of truth?

When updating a record, the older version seems to persist in localStore. It happens like this:

  1. In models index, go to edit form for an object.
  2. Perform the update.
  3. Be redirected to index, model hook fires but the enqueued update of step 2 didn't yet arrive to the server.
  4. Fetch the index JSON object, with the not-yet-updated name.
  5. Where's my update?

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.

emberSync doesn't save to firebase (via EmberFire)

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

Not compatible with ember-cli 0.1.1

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.

hasMany array is lost

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?

find records from server, relationship array not pushed to offline store

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;
  },

npm install on ember-cli 0.0.39 fails

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!

Find records from offline store, then from online store only if not found

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);
          });
        });
      },
    });

  },
}

offlineStore not saving when find new records from onlineStore

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.

Cannot read property 'lookup' of undefined

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?

No container in JSONSerializer

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)

post.async.get('comment')

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:

    • this.emberSync.find('posts') returns an array, not a promise (this could be inconvenient if we want to wait for the result before the next step).
    • this.emberSync.find(post,23) leads to a Ember.RSVP error (it expects a string for the id).

Many thanks for the excellent work,

L

emberSync.deleteRecord doesn't cleanup queue on success

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.