epf-source 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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);