syntheticore/declaire

View on GitHub
src/clientDataInterface.js

Summary

Maintainability
C
7 hrs
Test Coverage
var ClientStore = require('./clientStore.js');
var Collection = require('./collection.js');
var _ = require('./utils.js');


var ClientDataInterface = function(model) {
  var localStore = ClientStore(model.name);
  var url = model.url();
  var cache = {};
  
  // Create live instance from network or local data
  var init = function(data, meta) {
    // Construct instance
    var inst = model.create();
    inst.id = data._id;
    inst.data.remote = Collection.makeCollections(data, inst);
    inst.localId = meta && meta.localId
    // Update local storage on all network updates
    inst.on('fetch', function() {
      localStore.set(inst);
    });
    // Store in cache
    cache[inst.id || inst.localId] = inst; //XXX remove dead locals
    // Connect to pubsub if instance was persisted already
    if(inst.id) inst.connect();
    return inst;
  };

  return {
    all: function(options, cb) {
      // Build incomplete results from local storage
      var locals = localStore.query(options.query || {}, options.limit);
      var instances = _.map(locals, function(local) {
        var id = local.meta.id || local.meta.localId;
        return cache[id] ? cache[id] : init(local.data, local.meta);
      });
      // Use callback to return complete results from server
      _.ajax({url: url, data: options}).then(function(data) {
        cb(null, _.map(data, function(item) {
          if(cache[item._id]) {
            return cache[item._id];
          } else {
            var inst = init(item);
            // Store local copy on first fetch
            localStore.set(inst);
            return inst;
          }
        }));
      });
      // Return incomplete results synchronously
      return instances;
    },

    one: function(id, cb, raw) {
      var ajax = function(cbb) {
        _.ajax({url: url + '/' + id}).then(function(data) {
          cbb(data);
        }).catch(function() {
          cb('error', null);
        });
      };
      if(raw) {
        // Don't initialize instance and bypass cache in raw mode
        ajax(function(data) {
          cb(null, data);
        });
      } else {
        // Return live instance from cache if possible
        if(cache[id]) return cb(null, cache[id]);
        // Try to fetch object from local storage
        var data = localStore.get(id);
        if(data) {
          var inst = init(data);
          cb(null, inst);
          // Update instance from server anyways
          inst.fetch();
        } else {
          // Resort to ajax
          ajax(function(data) {
            var inst = init(data);
            // Store local copy on first fetch
            localStore.set(inst);
            cb(null, inst);
          });
        }
      }
    },

    create: function(inst, cb) {
      cache[inst.localId] = inst;
      // Save to local storage immediately in pending state
      localStore.set(inst, {_pending: 'create'});
      // Try to persist to server
      _.ajax({verb: 'POST', url: url, data: inst.serialize()}).then(function(data) {
        // Remove local storage entry under old ID
        localStore.delete(inst.localId);
        delete cache[inst.localId];
        cb(null, data);
        // Save again with final ID from server
        // inst.id = data._id;
        // inst.data.remote = data;
        localStore.set(inst);
        cache[inst.id] = inst;
      }).catch(function() {
        cb(null, inst.serialize());
      });
    },

    update: function(id, values, cb) {
      var inst = cache[id];
      // Save to local storage immediately in pending state
      localStore.set(inst, {_pending: 'update', _pendingValues: values});
      _.ajax({verb: 'POST', url: url + '/' + id, data: values}).then(function(updatedValues) {
        cb(null, updatedValues);
        localStore.set(inst, {_pending: null, _pendingValues: null});
      }).catch(function() {
        cb(null, values);
      });
    },

    delete: function(id, cb) {
      var inst = cache[id];
      if(inst) {
        // Mark object as collectable in case we're offline
        localStore.set(inst, {_pending: 'delete'});
        delete cache[id];
        _.ajax({verb: 'DELETE', url: url + '/' + id}).then(function() {
          // Remove on acknowlede
          localStore.delete(id);
        });
      }
      cb && cb();
    }
  };
};

module.exports = ClientDataInterface;



// var ClientDataInterface = function(model) {
//   var url = model.url();
//   var cache = {};
//   // Fetch object from cache
//   // or create first local representation otherwise
//   var init = function(data) {
//     if(cache[data._id]) return cache[data._id];
//     var inst = model.create();
//     inst.id = data._id;
//     inst.data.remote = data;
//     cache[inst.id] = inst;
//     return inst;
//   };
//   return {
//     all: function(options, cb) {
//       $.get(url)
//       .done(function(data) {
//         cb(null, _.map(data, function(item) {
//           return init(item);
//         }));
//       })
//       .fail(function() {
//         cb('error', null);
//       });
//     },

//     one: function(id, cb) {
//       $.get(url + '/' + id)
//       .done(function(data) {
//         cb(null, init(data));
//       })
//       .fail(function() {
//         cb('error', null);
//       });
//     },

//     create: function(inst, cb) {
//       $.post(url, {data: JSON.stringify(inst.serialize())})
//       .done(function(data) {
//         cache[data._id] = inst;
//         cb(null, data);
//       })
//       .fail(function() {
//         cb('error', null);
//       });
//     },

//     update: function(id, values, cb) {
//       $.post(url + '/' + id, {data: JSON.stringify(values)})
//       .done(function(data) {
//         cb(null, data);
//       })
//       .fail(function() {
//         cb('error', null);
//       });
//     },

//     delete: function(id, cb) {
//       delete cache[id];
//       $.ajax({url: url + '/' + id, type: 'DELETE'})
//       .always(function() {
//         cb && cb();
//       });
//     }
//   };
// };