epf-source 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. data/dist/epf.js +284 -145
  2. data/dist/epf.js.map +1 -0
  3. data/lib/epf/source.rb +4 -0
  4. metadata +3 -2
data/dist/epf.js CHANGED
@@ -34,7 +34,7 @@
34
34
  var cwd = '/';
35
35
  return {
36
36
  title: 'browser',
37
- version: 'v0.11.2',
37
+ version: 'v0.10.13',
38
38
  browser: true,
39
39
  env: {},
40
40
  argv: [],
@@ -55,7 +55,6 @@
55
55
  require('/lib/model/index.js', module);
56
56
  require('/lib/session/index.js', module);
57
57
  require('/lib/serializer/index.js', module);
58
- require('/lib/session/index.js', module);
59
58
  require('/lib/local/index.js', module);
60
59
  require('/lib/rest/index.js', module);
61
60
  });
@@ -166,6 +165,22 @@
166
165
  return null;
167
166
  }
168
167
  },
168
+ extractRevision: function (type, hash) {
169
+ var revision = this._revision(type);
170
+ if (hash.hasOwnProperty(revision) && hash[revision] !== null) {
171
+ return parseInt(hash[revision]);
172
+ } else {
173
+ return undefined;
174
+ }
175
+ },
176
+ extractClientRevision: function (type, hash) {
177
+ var revision = this._clientRevision(type);
178
+ if (hash.hasOwnProperty(revision) && hash[revision] !== null) {
179
+ return parseInt(hash[revision]);
180
+ } else {
181
+ return undefined;
182
+ }
183
+ },
169
184
  extractHasMany: function (type, hash, key) {
170
185
  return hash[key];
171
186
  },
@@ -442,6 +457,8 @@
442
457
  deserialize: mustImplement('deserialize'),
443
458
  extractId: mustImplement('extractId'),
444
459
  extractClientId: mustImplement('extractClientId'),
460
+ extractRevision: mustImplement('extractRevision'),
461
+ extractClientRevision: mustImplement('extractClientRevision'),
445
462
  extractAttribute: mustImplement('extractAttribute'),
446
463
  extractHasMany: mustImplement('extractHasMany'),
447
464
  extractBelongsTo: mustImplement('extractBelongsTo'),
@@ -449,6 +466,8 @@
449
466
  var model = this.createModel(type);
450
467
  set(model, 'id', this.extractId(type, hash));
451
468
  set(model, 'clientId', this.extractClientId(type, hash));
469
+ set(model, 'rev', this.extractRevision(type, hash));
470
+ set(model, 'clientRev', this.extractClientRevision(type, hash));
452
471
  this.deserializeAttributes(model, hash);
453
472
  this.deserializeRelationships(model, hash);
454
473
  return model;
@@ -573,12 +592,18 @@
573
592
  },
574
593
  serialize: function (record, options) {
575
594
  options = options || {};
576
- var serialized = this.createSerializedForm(), id;
595
+ var serialized = this.createSerializedForm(), id, rev, clientRev;
577
596
  if (options.includeId) {
578
597
  if (id = get(record, 'id')) {
579
598
  this._addId(serialized, record.constructor, id);
580
599
  }
581
600
  this._addClientId(serialized, record.constructor, get(record, 'clientId'));
601
+ if (rev = get(record, 'rev')) {
602
+ this._addRevision(serialized, record.constructor, get(record, 'rev'));
603
+ }
604
+ if (clientRev = get(record, 'clientRev')) {
605
+ this._addClientRevision(serialized, record.constructor, get(record, 'clientRev'));
606
+ }
582
607
  }
583
608
  if (options.includeType) {
584
609
  this.addType(serialized, record.constructor);
@@ -601,6 +626,12 @@
601
626
  serializeClientId: function (clientId) {
602
627
  return clientId;
603
628
  },
629
+ serializeRevision: function (rev) {
630
+ return rev;
631
+ },
632
+ serializeClientRevision: function (rev) {
633
+ return rev;
634
+ },
604
635
  addAttributes: function (data, record) {
605
636
  record.eachAttribute(function (name, attribute) {
606
637
  this._addAttribute(data, record, name, attribute.type);
@@ -632,6 +663,12 @@
632
663
  clientKey: function (type) {
633
664
  return 'client_id';
634
665
  },
666
+ revision: function (type) {
667
+ return 'rev';
668
+ },
669
+ clientRevision: function (type) {
670
+ return 'client_rev';
671
+ },
635
672
  keyForBelongsTo: function (type, name) {
636
673
  return this.keyForAttributeName(type, name);
637
674
  },
@@ -654,6 +691,22 @@
654
691
  return this.clientKey(type);
655
692
  }
656
693
  },
694
+ _revision: function (type) {
695
+ var config = this.configurationForType(type), revision = config && config.revision;
696
+ if (revision) {
697
+ return revision;
698
+ } else {
699
+ return this.revision(type);
700
+ }
701
+ },
702
+ _clientRevision: function (type) {
703
+ var config = this.configurationForType(type), clientRevision = config && config.clientRevision;
704
+ if (clientRevision) {
705
+ return clientRevision;
706
+ } else {
707
+ return this.clientRevision(type);
708
+ }
709
+ },
657
710
  _addAttribute: function (data, record, attributeName, attributeType) {
658
711
  var key = this._keyForAttributeName(record.constructor, attributeName);
659
712
  var value = get(record, attributeName);
@@ -667,6 +720,14 @@
667
720
  var clientKey = this._clientKey(type);
668
721
  this.addId(hash, clientKey, this.serializeClientId(id));
669
722
  },
723
+ _addRevision: function (hash, type, rev) {
724
+ var revision = this._revision(type);
725
+ this.addId(hash, revision, this.serializeRevision(rev));
726
+ },
727
+ _addClientRevision: function (hash, type, rev) {
728
+ var revision = this._clientRevision(type);
729
+ this.addId(hash, revision, this.serializeClientRevision(rev));
730
+ },
670
731
  _keyForAttributeName: function (type, name) {
671
732
  return this._keyFromMappingOrHook('keyForAttributeName', type, name);
672
733
  },
@@ -1311,6 +1372,8 @@
1311
1372
  Ep.ModelMixin = Ember.Mixin.create({
1312
1373
  id: null,
1313
1374
  clientId: null,
1375
+ rev: null,
1376
+ clientRev: 0,
1314
1377
  session: null,
1315
1378
  errors: null,
1316
1379
  isEqual: function (model) {
@@ -1357,17 +1420,15 @@
1357
1420
  Ep.Model = Ember.Object.extend(Ember.Copyable, Ep.ModelMixin, {
1358
1421
  isPromise: false,
1359
1422
  isProxy: false,
1423
+ isNew: true,
1360
1424
  isDeleted: false,
1361
1425
  isLoaded: true,
1362
- isNew: Ember.computed(function () {
1363
- return !get(this, 'id');
1364
- }).property('id'),
1365
- clientRevision: Ember.computed(function (key, value) {
1366
- if (arguments.length === 1) {
1367
- return 0;
1368
- }
1369
- return value;
1370
- }),
1426
+ isDirty: Ember.computed(function () {
1427
+ var session = get(this, 'session');
1428
+ if (!session)
1429
+ return false;
1430
+ return get(session, 'dirtyModels').contains(this);
1431
+ }).volatile(),
1371
1432
  diff: function (model) {
1372
1433
  var diffs = [];
1373
1434
  this.eachAttribute(function (name, meta) {
@@ -1456,7 +1517,10 @@
1456
1517
  dest.beginPropertyChanges();
1457
1518
  set(dest, 'id', get(this, 'id'));
1458
1519
  set(dest, 'clientId', get(this, 'clientId'));
1520
+ set(dest, 'rev', get(this, 'rev'));
1521
+ set(dest, 'clientRev', get(this, 'clientRev'));
1459
1522
  set(dest, 'errors', Ember.copy(get(this, 'errors')));
1523
+ set(dest, 'isNew', get(this, 'isNew'));
1460
1524
  set(dest, 'isDeleted', get(this, 'isDeleted'));
1461
1525
  this.eachAttribute(function (name, meta) {
1462
1526
  var left = get(this, name);
@@ -1800,7 +1864,10 @@
1800
1864
  Ep.ModelProxy = Ember.ObjectProxy.extend(Ember.Copyable, Ep.ModelMixin, {
1801
1865
  id: passThrough('id'),
1802
1866
  clientId: passThrough('clientId'),
1867
+ rev: passThrough('rev'),
1868
+ clientRev: passThrough('clientRev'),
1803
1869
  type: passThrough('type'),
1870
+ isDirty: false,
1804
1871
  isPromise: false,
1805
1872
  isLoaded: passThrough('isLoaded', false),
1806
1873
  isLoading: false,
@@ -1966,7 +2033,7 @@
1966
2033
  var data = {};
1967
2034
  data[root] = get(this, 'serializer').serialize(model, { includeId: true });
1968
2035
  return this.ajax(this.buildURL(root), 'POST', { data: data }).then(function (json) {
1969
- return Ember.run(adapter, 'didReceiveDataForCreate', json, model);
2036
+ return Ember.run(adapter, 'didReceiveData', json, model);
1970
2037
  }, function (xhr) {
1971
2038
  throw Ember.run(adapter, 'didError', xhr, model);
1972
2039
  });
@@ -2021,16 +2088,6 @@
2021
2088
  result = model;
2022
2089
  }
2023
2090
  });
2024
- return result || targetModel;
2025
- },
2026
- didReceiveDataForCreate: function (data, targetModel) {
2027
- var result = null;
2028
- this.processData(data, function (model) {
2029
- if (targetModel && model.isEqual(targetModel)) {
2030
- result = model;
2031
- }
2032
- });
2033
- set(targetModel, 'id', get(result, 'id'));
2034
2091
  return result;
2035
2092
  },
2036
2093
  didReceiveDataForLoad: function (data, type, id) {
@@ -2199,6 +2256,7 @@
2199
2256
  shadows.add(copy);
2200
2257
  }
2201
2258
  }
2259
+ this._embeddedManager.updateParents(model);
2202
2260
  }, this);
2203
2261
  },
2204
2262
  dirtyEmbeddedTree: function (model, models, shadows, session) {
@@ -2311,48 +2369,23 @@
2311
2369
  require.define('/lib/rest/operation_graph.js', function (module, exports, __dirname, __filename) {
2312
2370
  require('/lib/rest/operation.js', module);
2313
2371
  var get = Ember.get, set = Ember.set;
2314
- var Node = function (model) {
2315
- this.op = null;
2316
- this.children = Ember.Set.create();
2317
- this.parents = Ember.Set.create();
2318
- this.dirty = false;
2319
- this.dirtyEmbeddedChildren = false;
2320
- };
2321
- Node.prototype = {
2322
- addChild: function (childNode) {
2323
- this.children.add(childNode);
2324
- childNode.parents.add(this);
2325
- },
2326
- isRoot: function () {
2327
- return this.parents.every(function (parent) {
2328
- return !get(parent, 'dirty') && parent.isRoot();
2329
- });
2330
- },
2331
- toString: function (depth) {
2332
- if (!depth)
2333
- depth = 0;
2334
- var prefix = '';
2335
- for (var i = 0; i < depth; i++) {
2336
- prefix += ' ';
2337
- }
2338
- return prefix + this.op.toString() + this.children.map(function (child) {
2339
- return '\n' + child.toString(depth + 1);
2340
- });
2341
- }
2342
- };
2343
2372
  Ep.OperationGraph = Ember.Object.extend({
2344
2373
  models: null,
2345
2374
  shadows: null,
2346
- rootNodes: null,
2375
+ rootOps: null,
2347
2376
  adapter: null,
2348
- nodes: null,
2349
2377
  init: function () {
2350
- this.nodes = Ember.MapWithDefault.create({
2378
+ var graph = this;
2379
+ this.ops = Ember.MapWithDefault.create({
2351
2380
  defaultValue: function (model) {
2352
- return new Node(model);
2381
+ return Ep.Operation.create({
2382
+ model: model,
2383
+ graph: graph,
2384
+ adapter: get(graph, 'adapter')
2385
+ });
2353
2386
  }
2354
2387
  });
2355
- this.rootNodes = Ember.Set.create();
2388
+ this.rootOps = Ember.Set.create();
2356
2389
  this.build();
2357
2390
  },
2358
2391
  perform: function () {
@@ -2362,63 +2395,60 @@
2362
2395
  var adapter = get(this, 'adapter');
2363
2396
  var models = get(this, 'models');
2364
2397
  var shadows = get(this, 'shadows');
2365
- var rootNodes = get(this, 'rootNodes');
2366
- var nodes = get(this, 'nodes');
2398
+ var rootOps = get(this, 'rootOps');
2399
+ var ops = get(this, 'ops');
2367
2400
  models.forEach(function (model) {
2368
2401
  if (!get(model, 'isLoaded')) {
2369
2402
  return;
2370
2403
  }
2371
2404
  var shadow = shadows.getModel(model);
2372
- var node = nodes.get(model);
2373
- node.op = Ep.Operation.create({
2374
- model: model,
2375
- shadow: shadow,
2376
- graph: this,
2377
- adapter: adapter
2378
- });
2379
- node.dirty = node.dirty || !!get(node.op, 'dirtyType');
2380
- if (node.dirty && adapter.isEmbedded(model) && !get(model, 'isNew')) {
2381
- var root = adapter.findEmbeddedRoot(model, models);
2382
- var rootNode = nodes.get(root);
2383
- rootNode.dirty = true;
2384
- rootNode.dirtyEmbeddedChildren = true;
2385
- }
2386
- var rels = get(node.op, 'dirtyRelationships');
2405
+ Ember.assert('Shadow does not exist for non-new model', shadow || get(model, 'isNew'));
2406
+ var op = ops.get(model);
2407
+ set(op, 'shadow', shadow);
2408
+ var isEmbedded = adapter.isEmbedded(model);
2409
+ if (get(op, 'isDirty') && isEmbedded) {
2410
+ var rootModel = adapter.findEmbeddedRoot(model, models);
2411
+ var rootOp = this.getOp(rootModel);
2412
+ set(rootOp, 'force', true);
2413
+ var parentModel = adapter._embeddedManager.findParent(model);
2414
+ var parentOp = this.getOp(parentModel);
2415
+ parentOp.addChild(op);
2416
+ }
2417
+ var rels = get(op, 'dirtyRelationships');
2387
2418
  for (var i = 0; i < rels.length; i++) {
2388
2419
  var d = rels[i];
2389
2420
  var name = d.name;
2390
2421
  var parentModel = model.get(name) || shadows.getModel(d.oldValue);
2391
2422
  if (parentModel) {
2392
- parentModel = models.getModel(parentModel);
2393
- var parentNode = nodes.get(parentModel);
2394
- parentNode.addChild(node);
2423
+ var parentOp = this.getOp(parentModel);
2424
+ parentOp.addChild(op);
2395
2425
  }
2396
2426
  }
2397
2427
  }, this);
2398
- nodes.forEach(function (model, node) {
2399
- if (node.dirty && node.isRoot()) {
2400
- rootNodes.add(node);
2428
+ ops.forEach(function (model, op) {
2429
+ if (get(op, 'isDirty') && get(op, 'isRoot')) {
2430
+ rootOps.add(op);
2401
2431
  }
2402
2432
  }, this);
2403
2433
  },
2434
+ getOp: function (model) {
2435
+ var models = get(this, 'models');
2436
+ return this.ops.get(models.getModel(model));
2437
+ },
2404
2438
  createPromise: function () {
2405
- var rootNodes = get(this, 'rootNodes'), adapter = get(this, 'adapter'), cumulative = [];
2406
- function createNestedPromise(node) {
2407
- var promise = node.op.perform(node.dirtyEmbeddedChildren);
2439
+ var rootOps = get(this, 'rootOps'), adapter = get(this, 'adapter'), cumulative = [];
2440
+ function createNestedPromise(op) {
2441
+ var promise = op.perform();
2408
2442
  promise = promise.then(function (model) {
2409
- if (model === get(node.op, 'model')) {
2410
- cumulative.push(model.lazyCopy());
2411
- } else {
2412
- cumulative.push(model);
2413
- }
2443
+ cumulative.push(model);
2414
2444
  return model;
2415
2445
  }, function (model) {
2416
2446
  cumulative.push(model);
2417
2447
  throw model;
2418
2448
  });
2419
- if (node.children.length > 0) {
2449
+ if (op.children.length > 0) {
2420
2450
  promise = promise.then(function (model) {
2421
- var childPromises = node.children.map(createNestedPromise);
2451
+ var childPromises = op.children.map(createNestedPromise);
2422
2452
  return Ember.RSVP.all(childPromises).then(function (models) {
2423
2453
  adapter.rebuildRelationships(models, model);
2424
2454
  return model;
@@ -2427,7 +2457,7 @@
2427
2457
  }
2428
2458
  return promise;
2429
2459
  }
2430
- return Ember.RSVP.all(rootNodes.map(createNestedPromise)).then(function () {
2460
+ return Ember.RSVP.all(rootOps.map(createNestedPromise)).then(function () {
2431
2461
  return cumulative;
2432
2462
  }, function (err) {
2433
2463
  throw cumulative;
@@ -2435,9 +2465,9 @@
2435
2465
  },
2436
2466
  toStringExtension: function () {
2437
2467
  var result = '';
2438
- var rootNodes = get(this, 'rootNodes');
2439
- rootNodes.forEach(function (node) {
2440
- result += '\n' + node.toString(1);
2468
+ var rootOps = get(this, 'rootOps');
2469
+ rootOps.forEach(function (op) {
2470
+ result += '\n' + op.toString(1);
2441
2471
  });
2442
2472
  return result + '\n';
2443
2473
  }
@@ -2449,6 +2479,8 @@
2449
2479
  model: null,
2450
2480
  shadow: null,
2451
2481
  adapter: null,
2482
+ _promise: null,
2483
+ force: false,
2452
2484
  init: function () {
2453
2485
  this.children = Ember.Set.create();
2454
2486
  this.parents = Ember.Set.create();
@@ -2478,6 +2510,9 @@
2478
2510
  return rels;
2479
2511
  }),
2480
2512
  isDirty: Ember.computed(function () {
2513
+ return !!get(this, 'dirtyType');
2514
+ }),
2515
+ isDirtyFromUpdates: Ember.computed(function () {
2481
2516
  var model = get(this, 'model'), shadow = get(this, 'shadow'), adapter = get(this, 'adapter');
2482
2517
  var diff = model.diff(shadow);
2483
2518
  var dirty = false;
@@ -2498,17 +2533,20 @@
2498
2533
  return 'created';
2499
2534
  } else if (get(model, 'isDeleted')) {
2500
2535
  return 'deleted';
2501
- } else if (get(this, 'isDirty')) {
2536
+ } else if (get(this, 'isDirtyFromUpdates') || get(this, 'force')) {
2502
2537
  return 'updated';
2503
2538
  }
2504
2539
  }),
2505
- perform: function (forceUpdate) {
2540
+ perform: function () {
2541
+ if (this._promise)
2542
+ return this._promise;
2506
2543
  var adapter = get(this, 'adapter'), dirtyType = get(this, 'dirtyType'), model = get(this, 'model'), shadow = get(this, 'shadow'), promise;
2507
- if (!dirtyType && forceUpdate) {
2508
- dirtyType = 'updated';
2509
- }
2510
2544
  if (!dirtyType || !adapter.shouldSave(model)) {
2511
- promise = Ember.RSVP.resolve(model);
2545
+ if (adapter.isEmbedded(model)) {
2546
+ promise = this._promiseFromEmbeddedParent();
2547
+ } else {
2548
+ promise = Ember.RSVP.resolve();
2549
+ }
2512
2550
  } else if (dirtyType === 'created') {
2513
2551
  promise = adapter.create(model);
2514
2552
  } else if (dirtyType === 'updated') {
@@ -2516,17 +2554,61 @@
2516
2554
  } else if (dirtyType === 'deleted') {
2517
2555
  promise = adapter.deleteModel(model);
2518
2556
  }
2519
- return promise.then(null, function (model) {
2557
+ promise = promise.then(function (serverModel) {
2558
+ if (!get(model, 'id')) {
2559
+ set(model, 'id', get(serverModel, 'id'));
2560
+ }
2561
+ if (!serverModel) {
2562
+ serverModel = model;
2563
+ } else if (!get(serverModel, 'clientRev')) {
2564
+ set(serverModel, 'clientRev', get(model, 'clientRev'));
2565
+ }
2566
+ return serverModel;
2567
+ }, function (serverModel) {
2520
2568
  if (shadow) {
2521
- shadow.set('errors', model.get('errors'));
2569
+ shadow.set('errors', serverModel.get('errors'));
2522
2570
  throw shadow;
2523
2571
  }
2524
- throw model;
2572
+ throw serverModel;
2573
+ });
2574
+ return this._promise = promise;
2575
+ },
2576
+ _embeddedParent: Ember.computed(function () {
2577
+ var model = get(this, 'model'), parentModel = get(this, 'adapter')._embeddedManager.findParent(model), graph = get(this, 'graph');
2578
+ Ember.assert('Embedded parent does not exist!', parentModel);
2579
+ return graph.getOp(parentModel);
2580
+ }),
2581
+ _promiseFromEmbeddedParent: function () {
2582
+ var serializer = get(this, 'adapter.serializer');
2583
+ var model = this.model;
2584
+ function findInParent(parentModel) {
2585
+ var res = null;
2586
+ serializer.eachEmbeddedRecord(parentModel, function (child, embeddedType) {
2587
+ if (res)
2588
+ return;
2589
+ if (child.isEqual(model))
2590
+ res = child;
2591
+ });
2592
+ return res;
2593
+ }
2594
+ return get(this, '_embeddedParent').perform().then(function (parent) {
2595
+ return findInParent(parent);
2596
+ }, function (parent) {
2597
+ throw findInParent(parent);
2525
2598
  });
2526
2599
  },
2527
2600
  toStringExtension: function () {
2528
2601
  return '( ' + get(this, 'dirtyType') + ' ' + get(this, 'model') + ' )';
2529
- }
2602
+ },
2603
+ addChild: function (child) {
2604
+ this.children.add(child);
2605
+ child.parents.add(this);
2606
+ },
2607
+ isRoot: Ember.computed(function () {
2608
+ return this.parents.every(function (parent) {
2609
+ return !get(parent, 'isDirty') && get(parent, 'isRoot');
2610
+ });
2611
+ }).volatile()
2530
2612
  });
2531
2613
  });
2532
2614
  require.define('/lib/rest/embedded_manager.js', function (module, exports, __dirname, __filename) {
@@ -2878,34 +2960,70 @@
2878
2960
  var get = Ember.get, set = Ember.set;
2879
2961
  Ep.Session.reopen({
2880
2962
  merge: function (model, strategy) {
2881
- var shadows = get(this, 'shadows'), newModels = get(this, 'newModels');
2882
2963
  this.reifyClientId(model);
2883
- if (!strategy)
2884
- strategy = get(this, 'mergeStrategy').create({ session: this });
2885
- if (!get(model, 'hasErrors')) {
2886
- var merged;
2887
- this.suspendDirtyChecking(function () {
2888
- merged = this.mergeModel(model, strategy);
2889
- }, this);
2890
- if (shadows.contains(model) && get(model, 'isLoaded')) {
2891
- shadows.add(model);
2964
+ if (get(model, 'hasErrors')) {
2965
+ return this._mergeError(model, strategy);
2966
+ } else {
2967
+ return this._mergeSuccess(model, strategy);
2968
+ }
2969
+ },
2970
+ _mergeSuccess: function (model, strategy) {
2971
+ var models = get(this, 'models'), shadows = get(this, 'shadows'), newModels = get(this, 'newModels'), originals = get(this, 'originals'), merged, ancestor, existing = models.getModel(model);
2972
+ if (existing && this._containsRev(existing, model)) {
2973
+ return existing;
2974
+ }
2975
+ var hasClientChanges = !existing || this._containsClientRev(model, existing);
2976
+ if (hasClientChanges) {
2977
+ ancestor = shadows.getModel(model);
2978
+ } else {
2979
+ ancestor = originals.getModel(model);
2980
+ }
2981
+ this.suspendDirtyChecking(function () {
2982
+ merged = this._mergeModel(existing, ancestor, model, strategy);
2983
+ }, this);
2984
+ if (hasClientChanges) {
2985
+ if (get(merged, 'isDeleted')) {
2986
+ this.remove(merged);
2987
+ } else {
2988
+ if (shadows.contains(model) && get(model, 'isLoaded')) {
2989
+ shadows.add(model);
2990
+ }
2991
+ originals.remove(model);
2992
+ if (!get(merged, 'isNew')) {
2993
+ newModels.remove(merged);
2994
+ }
2892
2995
  }
2893
- newModels.remove(model);
2894
2996
  } else {
2997
+ }
2998
+ return merged;
2999
+ },
3000
+ _mergeError: function (model, strategy) {
3001
+ var models = get(this, 'models'), shadows = get(this, 'shadows'), newModels = get(this, 'newModels'), originals = get(this, 'originals'), merged, ancestor, existing = models.getModel(model);
3002
+ if (!existing) {
3003
+ Ember.assert('Errors returned for non-existant model: ' + model.toString(), model instanceof Ep.LoadError);
3004
+ return model;
3005
+ }
3006
+ ancestor = originals.getModel(model);
3007
+ if (ancestor && !this._containsRev(existing, model)) {
2895
3008
  this.suspendDirtyChecking(function () {
2896
- merged = this.mergeErrors(model, strategy);
3009
+ merged = this._mergeModel(existing, ancestor, model, strategy);
2897
3010
  }, this);
2898
- shadows.add(model);
3011
+ } else {
3012
+ merged = existing;
2899
3013
  }
3014
+ set(merged, 'errors', Ember.copy(get(model, 'errors')));
3015
+ shadows.add(model);
3016
+ originals.remove(model);
2900
3017
  return merged;
2901
3018
  },
2902
- mergeModel: function (model, strategy) {
2903
- var dest = this.getModel(model);
3019
+ _mergeModel: function (dest, ancestor, model, strategy) {
3020
+ if (!strategy)
3021
+ strategy = get(this, 'mergeStrategy').create({ session: this });
2904
3022
  if (model === dest) {
2905
3023
  return model;
2906
3024
  }
2907
3025
  if (get(model, 'isPromise')) {
2908
- return this.mergePromise(model, dest, strategy);
3026
+ return this._mergePromise(dest, ancestor, model, strategy);
2909
3027
  }
2910
3028
  var promise;
2911
3029
  if (dest && get(dest, 'isPromise')) {
@@ -2918,18 +3036,24 @@
2918
3036
  promise.resolve(dest);
2919
3037
  }
2920
3038
  }
3039
+ if (!get(model, 'hasErrors')) {
3040
+ set(dest, 'isNew', false);
3041
+ }
2921
3042
  set(dest, 'id', get(model, 'id'));
2922
3043
  set(dest, 'clientId', get(model, 'clientId'));
3044
+ set(dest, 'rev', get(model, 'rev'));
2923
3045
  set(dest, 'isDeleted', get(model, 'isDeleted'));
2924
- set(dest, 'errors', Ember.copy(get(model, 'errors')));
2925
3046
  this.adopt(dest);
2926
- strategy.merge(model, dest);
3047
+ if (!ancestor) {
3048
+ ancestor = dest;
3049
+ }
3050
+ strategy.merge(dest, ancestor, model);
2927
3051
  return dest;
2928
3052
  },
2929
- mergePromise: function (promise, dest, strategy) {
3053
+ _mergePromise: function (dest, ancestor, promise, strategy) {
2930
3054
  var content = get(promise, 'content');
2931
3055
  if (content) {
2932
- return this.merge(content, strategy);
3056
+ return this.merge(dest, ancestor, content, strategy);
2933
3057
  }
2934
3058
  if (!dest) {
2935
3059
  dest = promise.lazyCopy();
@@ -2937,14 +3061,15 @@
2937
3061
  }
2938
3062
  return dest;
2939
3063
  },
2940
- mergeErrors: function (model, strategy) {
2941
- var dest = this.getModel(model);
2942
- if (!dest) {
2943
- Ember.assert('Errors returned for non-existant model: ' + model.toString(), model instanceof Ep.LoadError);
2944
- return model;
2945
- }
2946
- set(dest, 'errors', Ember.copy(get(model, 'errors')));
2947
- return dest;
3064
+ _containsRev: function (modelA, modelB) {
3065
+ if (!get(modelA, 'rev'))
3066
+ return false;
3067
+ if (!get(modelB, 'rev'))
3068
+ return false;
3069
+ return get(modelA, 'rev') >= get(modelB, 'rev');
3070
+ },
3071
+ _containsClientRev: function (modelA, modelB) {
3072
+ return get(modelA, 'clientRev') >= get(modelB, 'clientRev');
2948
3073
  }
2949
3074
  });
2950
3075
  });
@@ -2965,6 +3090,7 @@
2965
3090
  this.collectionManager = Ep.CollectionManager.create();
2966
3091
  this.belongsToManager = Ep.BelongsToManager.create();
2967
3092
  this.shadows = Ep.ModelSet.create();
3093
+ this.originals = Ep.ModelSet.create();
2968
3094
  this.newModels = Ep.ModelSet.create();
2969
3095
  },
2970
3096
  create: function (type, hash) {
@@ -3042,6 +3168,11 @@
3042
3168
  }, this);
3043
3169
  return dest;
3044
3170
  },
3171
+ remove: function (model) {
3172
+ get(this, 'models').remove(model);
3173
+ get(this, 'shadows').remove(model);
3174
+ get(this, 'originals').remove(model);
3175
+ },
3045
3176
  update: function (model) {
3046
3177
  this.reifyClientId(model);
3047
3178
  if (get(model, 'isProxy')) {
@@ -3135,7 +3266,10 @@
3135
3266
  });
3136
3267
  },
3137
3268
  flush: function () {
3138
- var session = this, dirtyModels = get(this, 'dirtyModels'), shadows = get(this, 'shadows');
3269
+ var session = this, dirtyModels = get(this, 'dirtyModels'), newModels = get(this, 'newModels'), shadows = get(this, 'shadows');
3270
+ dirtyModels.forEach(function (model) {
3271
+ model.incrementProperty('clientRev');
3272
+ }, this);
3139
3273
  var promise = this.adapter.flush(this).then(function (models) {
3140
3274
  var res = models.map(function (model) {
3141
3275
  return session.merge(model);
@@ -3148,8 +3282,14 @@
3148
3282
  throw res;
3149
3283
  });
3150
3284
  dirtyModels.forEach(function (model) {
3151
- this.shadows.add(model.copy());
3285
+ var original = this.originals.getModel(model);
3286
+ var shadow = this.shadows.getModel(model);
3287
+ if (shadow && (!original || original.get('rev') < shadow.get('rev'))) {
3288
+ this.originals.add(shadow);
3289
+ }
3290
+ this.shadows.remove(model);
3152
3291
  }, this);
3292
+ newModels.clear();
3153
3293
  return promise;
3154
3294
  },
3155
3295
  getModel: function (model) {
@@ -3189,6 +3329,7 @@
3189
3329
  this.collectionManager.destroy();
3190
3330
  this.belongsToManager.destroy();
3191
3331
  this.shadows.destroy();
3332
+ this.originals.destroy();
3192
3333
  this.newModels.destroy();
3193
3334
  },
3194
3335
  dirtyModels: Ember.computed(function () {
@@ -3243,23 +3384,21 @@
3243
3384
  init: function () {
3244
3385
  this.cache = Ep.ModelSet.create();
3245
3386
  },
3246
- merge: function (theirs, ours) {
3387
+ merge: function (ours, ancestor, theirs) {
3247
3388
  if (this.cache.contains(ours))
3248
3389
  return ours;
3249
3390
  this.cache.addObject(theirs);
3250
3391
  ours.beginPropertyChanges();
3251
- var session = get(this, 'session');
3252
- var original = session.getShadow(ours);
3253
- this.mergeAttributes(theirs, ours, original);
3254
- this.mergeRelationships(theirs, ours, original);
3392
+ this.mergeAttributes(ours, ancestor, theirs);
3393
+ this.mergeRelationships(ours, ancestor, theirs);
3255
3394
  ours.endPropertyChanges();
3256
3395
  return ours;
3257
3396
  },
3258
- mergeAttributes: function (theirs, ours, original) {
3397
+ mergeAttributes: function (ours, ancestor, theirs) {
3259
3398
  ours.eachAttribute(function (name, meta) {
3260
3399
  var oursValue = get(ours, name);
3261
3400
  var theirsValue = get(theirs, name);
3262
- var originalValue = get(original, name);
3401
+ var originalValue = get(ancestor, name);
3263
3402
  if (oursValue === theirsValue)
3264
3403
  return;
3265
3404
  if (oursValue === originalValue) {
@@ -3267,13 +3406,13 @@
3267
3406
  }
3268
3407
  });
3269
3408
  },
3270
- mergeRelationships: function (theirs, ours, original) {
3409
+ mergeRelationships: function (ours, ancestor, theirs) {
3271
3410
  var session = get(this, 'session');
3272
3411
  ours.eachRelationship(function (name, relationship) {
3273
3412
  if (relationship.kind === 'belongsTo') {
3274
3413
  var oursValue = get(ours, name);
3275
3414
  var theirsValue = get(theirs, name);
3276
- var originalValue = get(original, name);
3415
+ var originalValue = get(ancestor, name);
3277
3416
  var merged = mergeIfPresent(session, theirsValue, this);
3278
3417
  if (isEqual(oursValue, originalValue)) {
3279
3418
  set(ours, name, merged);
@@ -3281,7 +3420,7 @@
3281
3420
  } else if (relationship.kind === 'hasMany') {
3282
3421
  var theirChildren = get(theirs, name);
3283
3422
  var ourChildren = get(ours, name);
3284
- var originalChildren = get(original, name);
3423
+ var originalChildren = get(ancestor, name);
3285
3424
  if (isEqual(ourChildren, originalChildren)) {
3286
3425
  var existing = Ep.ModelSet.create();
3287
3426
  existing.addObjects(ourChildren);
@@ -3310,7 +3449,7 @@
3310
3449
  init: function () {
3311
3450
  this.cache = Ep.ModelSet.create();
3312
3451
  },
3313
- merge: function (model, dest) {
3452
+ merge: function (dest, ancestor, model) {
3314
3453
  if (this.cache.contains(model))
3315
3454
  return dest;
3316
3455
  this.cache.addObject(model);