backbone-relational-rails 0.8.6 → 0.8.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a358ff7e2c4cabae8dbddc3fbe53df8a87ff168c
4
- data.tar.gz: 1c0de4ed22b1e62f19050ddb1811ae417e026adf
3
+ metadata.gz: 383e74d8144f6aa8fde07a6dcfbd2a924559c5b5
4
+ data.tar.gz: 4971d471d1dc3134765e99980ec3adcd68e6638a
5
5
  SHA512:
6
- metadata.gz: 14f7c8b8e3385e59d57cc4de3c5a114559bfaa6af8d79de6e5e3004a4cd78a645e55d1539a582818cf0c570d91e4024090aa9ccaaef9f248641cf8f8a37cf46e
7
- data.tar.gz: f18037d420056d0e8be90af1b8a5dbcdeea84fe49905ed24f8174caa85665938ba0986cae98cc46a410c5477a2b7836ca4ce702de06a9a313d1efe53749424eb
6
+ metadata.gz: be19e63711b13f668de3b1f0bc4d9308ad3a1570c7c52dd7568bf58c9f874f8ffb6be71b916e034830a07d3882fe3b5e1f7ee9437b06f601521b237edbc40215
7
+ data.tar.gz: 92937ff96c6069ac98c879176a8983fbef8dfe3cd7fcd3972260c1bed2aeb2872df2ca5a8e4b9d0dcf27483ffe630030bf5f86b36b9996d09fa4a4292405efc8
data/README.md CHANGED
@@ -18,7 +18,7 @@ Add the following directive to your Javascript manifest file (application.js):
18
18
 
19
19
  ## Versioning
20
20
 
21
- backbone-relational-rails 0.8.6 == Backbone-relational 0.8.6
21
+ backbone-relational-rails 0.8.7 == Backbone-relational 0.8.7
22
22
 
23
23
  Every attempt is made to mirror the currently shipping Backbone-relational version number wherever possible.
24
24
  The major, minor, and patch version numbers will always represent the Backbone-relational version. Should a gem
@@ -1,7 +1,7 @@
1
1
  module Backbone
2
2
  module Relational
3
3
  module Rails
4
- VERSION = "0.8.6"
4
+ VERSION = "0.8.7"
5
5
  end
6
6
  end
7
7
  end
@@ -1,11 +1,49 @@
1
1
  /* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab: */
2
2
  /**
3
- * Backbone-relational.js 0.8.6
3
+ * Backbone-relational.js 0.8.7
4
4
  * (c) 2011-2013 Paul Uithol and contributors (https://github.com/PaulUithol/Backbone-relational/graphs/contributors)
5
- *
5
+ *
6
6
  * Backbone-relational may be freely distributed under the MIT license; see the accompanying LICENSE.txt.
7
7
  * For details and documentation: https://github.com/PaulUithol/Backbone-relational.
8
8
  * Depends on Backbone (and thus on Underscore as well): https://github.com/documentcloud/backbone.
9
+ *
10
+ * Example:
11
+ *
12
+ Zoo = Backbone.RelationalModel.extend( {
13
+ relations: [ {
14
+ type: Backbone.HasMany,
15
+ key: 'animals',
16
+ relatedModel: 'Animal',
17
+ reverseRelation: {
18
+ key: 'livesIn',
19
+ includeInJSON: 'id'
20
+ // 'relatedModel' is automatically set to 'Zoo'; the 'relationType' to 'HasOne'.
21
+ }
22
+ } ],
23
+
24
+ toString: function() {
25
+ return this.get( 'name' );
26
+ }
27
+ } );
28
+
29
+ Animal = Backbone.RelationalModel.extend( {
30
+ toString: function() {
31
+ return this.get( 'species' );
32
+ }
33
+ } );
34
+
35
+ // Creating the zoo will give it a collection with one animal in it: the monkey.
36
+ // The animal created after that has a relation `livesIn` that points to the zoo it's currently associated with.
37
+ // If you instantiate (or fetch) the zebra later, it will automatically be added.
38
+
39
+ var zoo = new Zoo( {
40
+ name: 'Artis',
41
+ animals: [ { id: 'monkey-1', species: 'Chimp' }, 'lion-1', 'zebra-1' ]
42
+ } );
43
+
44
+ var lion = new Animal( { id: 'lion-1', species: 'Lion' } ),
45
+ monkey = zoo.get( 'animals' ).first(),
46
+ sameZoo = lion.get( 'livesIn' );
9
47
  */
10
48
  ( function( undefined ) {
11
49
  "use strict";
@@ -224,7 +262,7 @@
224
262
  return val === rel[ key ];
225
263
  });
226
264
  });
227
-
265
+
228
266
  if ( !exists && relation.model && relation.type ) {
229
267
  this._reverseRelations.push( relation );
230
268
  this._addRelation( relation.model, relation );
@@ -305,20 +343,20 @@
305
343
  if ( type instanceof Backbone.RelationalModel ) {
306
344
  type = type.constructor;
307
345
  }
308
-
346
+
309
347
  var rootModel = type;
310
348
  while ( rootModel._superModel ) {
311
349
  rootModel = rootModel._superModel;
312
350
  }
313
-
351
+
314
352
  var coll = _.find( this._collections, function(item) {
315
- return item.model === rootModel;
353
+ return item.model === rootModel;
316
354
  });
317
-
355
+
318
356
  if ( !coll && create !== false ) {
319
357
  coll = this._createCollection( rootModel );
320
358
  }
321
-
359
+
322
360
  return coll;
323
361
  },
324
362
 
@@ -346,20 +384,20 @@
346
384
 
347
385
  _createCollection: function( type ) {
348
386
  var coll;
349
-
387
+
350
388
  // If 'type' is an instance, take its constructor
351
389
  if ( type instanceof Backbone.RelationalModel ) {
352
390
  type = type.constructor;
353
391
  }
354
-
392
+
355
393
  // Type should inherit from Backbone.RelationalModel.
356
394
  if ( type.prototype instanceof Backbone.RelationalModel ) {
357
395
  coll = new Backbone.Collection();
358
396
  coll.model = type;
359
-
397
+
360
398
  this._collections.push( coll );
361
399
  }
362
-
400
+
363
401
  return coll;
364
402
  },
365
403
 
@@ -395,9 +433,9 @@
395
433
  * @param {String|Number|Object|Backbone.RelationalModel} item
396
434
  */
397
435
  find: function( type, item ) {
398
- var id = this.resolveIdForItem( type, item );
399
- var coll = this.getCollection( type );
400
-
436
+ var id = this.resolveIdForItem( type, item ),
437
+ coll = this.getCollection( type );
438
+
401
439
  // Because the found object could be of any of the type's superModel
402
440
  // types, only return it if it's actually of the type asked for.
403
441
  if ( coll ) {
@@ -461,9 +499,16 @@
461
499
  /**
462
500
  * Remove a 'model' from the store.
463
501
  * @param {Backbone.RelationalModel} model
502
+ * @param {Backbone.Collection} [collection]
503
+ * @param {Object} [options]
464
504
  */
465
505
  unregister: function( model, collection, options ) {
466
506
  this.stopListening( model );
507
+
508
+ _.each( model.getRelations(), function( rel ) {
509
+ rel.stopListening();
510
+ });
511
+
467
512
  var coll = this.getCollection( model );
468
513
  coll && coll.remove( model, options );
469
514
  },
@@ -673,7 +718,7 @@
673
718
  }
674
719
  }, this );
675
720
  }, this );
676
-
721
+
677
722
  return reverseRelations;
678
723
  },
679
724
 
@@ -690,7 +735,7 @@
690
735
  else if ( this instanceof Backbone.HasMany ) {
691
736
  this.setRelated( this._prepareCollection() );
692
737
  }
693
-
738
+
694
739
  _.each( this.getReverseRelations(), function( relation ) {
695
740
  relation.removeRelated( this.instance );
696
741
  }, this );
@@ -733,7 +778,7 @@
733
778
  }
734
779
 
735
780
  // Nullify `keyId` if we have a related model; in case it was already part of the relation
736
- if ( this.related ) {
781
+ if ( related ) {
737
782
  this.keyId = null;
738
783
  }
739
784
 
@@ -760,19 +805,19 @@
760
805
  }
761
806
  this.acquire();
762
807
  options = options ? _.clone( options ) : {};
763
-
808
+
764
809
  // 'options.__related' is set by 'addRelated'/'removeRelated'. If it is set, the change
765
- // is the result of a call from a relation. If it's not, the change is the result of
810
+ // is the result of a call from a relation. If it's not, the change is the result of
766
811
  // a 'set' call on this.instance.
767
812
  var changed = _.isUndefined( options.__related ),
768
813
  oldRelated = changed ? this.related : options.__related;
769
-
814
+
770
815
  if ( changed ) {
771
816
  this.setKeyContents( attr );
772
817
  var related = this.findRelated( options );
773
818
  this.setRelated( related );
774
819
  }
775
-
820
+
776
821
  // Notify old 'related' object of the terminated relation
777
822
  if ( oldRelated && this.related !== oldRelated ) {
778
823
  _.each( this.getReverseRelations( oldRelated ), function( relation ) {
@@ -786,7 +831,7 @@
786
831
  _.each( this.getReverseRelations(), function( relation ) {
787
832
  relation.addRelated( this.instance, options );
788
833
  }, this );
789
-
834
+
790
835
  // Fire the 'change:<key>' event if 'related' was updated
791
836
  if ( !options.silent && this.related !== oldRelated ) {
792
837
  var dit = this;
@@ -826,7 +871,7 @@
826
871
  if ( !this.related ) {
827
872
  return;
828
873
  }
829
-
874
+
830
875
  if ( model === this.related ) {
831
876
  var oldRelated = this.related || null;
832
877
  this.setRelated( null );
@@ -847,7 +892,7 @@
847
892
 
848
893
  initialize: function( opts ) {
849
894
  this.listenTo( this.instance, 'relational:change:' + this.key, this.onChange );
850
-
895
+
851
896
  // Handle a custom 'collectionType'
852
897
  this.collectionType = this.options.collectionType;
853
898
  if ( _.isFunction( this.collectionType ) && this.collectionType !== Backbone.Collection && !( this.collectionType.prototype instanceof Backbone.Collection ) ) {
@@ -883,10 +928,10 @@
883
928
  }
884
929
 
885
930
  collection.model = this.relatedModel;
886
-
931
+
887
932
  if ( this.options.collectionKey ) {
888
933
  var key = this.options.collectionKey === true ? this.options.reverseRelation.key : this.options.collectionKey;
889
-
934
+
890
935
  if ( collection[ key ] && collection[ key ] !== this.instance ) {
891
936
  if ( Backbone.Relational.showWarnings && typeof console !== 'undefined' ) {
892
937
  console.warn( 'Relation=%o; collectionKey=%s already exists on collection=%o', this, key, this.options.collectionKey );
@@ -900,7 +945,7 @@
900
945
  this.listenTo( collection, 'relational:add', this.handleAddition )
901
946
  .listenTo( collection, 'relational:remove', this.handleRemoval )
902
947
  .listenTo( collection, 'relational:reset', this.handleReset );
903
-
948
+
904
949
  return collection;
905
950
  },
906
951
 
@@ -1009,7 +1054,7 @@
1009
1054
  //console.debug('handleAddition called; args=%o', arguments);
1010
1055
  options = options ? _.clone( options ) : {};
1011
1056
  this.changed = true;
1012
-
1057
+
1013
1058
  _.each( this.getReverseRelations( model ), function( relation ) {
1014
1059
  relation.addRelated( this.instance, options );
1015
1060
  }, this );
@@ -1029,11 +1074,11 @@
1029
1074
  //console.debug('handleRemoval called; args=%o', arguments);
1030
1075
  options = options ? _.clone( options ) : {};
1031
1076
  this.changed = true;
1032
-
1077
+
1033
1078
  _.each( this.getReverseRelations( model ), function( relation ) {
1034
1079
  relation.removeRelated( this.instance, null, options );
1035
1080
  }, this );
1036
-
1081
+
1037
1082
  var dit = this;
1038
1083
  !options.silent && Backbone.Relational.eventQueue.add( function() {
1039
1084
  dit.instance.trigger( 'remove:' + dit.key, model, dit.related, options );
@@ -1123,7 +1168,7 @@
1123
1168
  }
1124
1169
 
1125
1170
  Backbone.Relational.store.processOrphanRelations();
1126
-
1171
+
1127
1172
  this._queue = new Backbone.BlockingQueue();
1128
1173
  this._queue.block();
1129
1174
  Backbone.Relational.eventQueue.block();
@@ -1202,7 +1247,7 @@
1202
1247
  this.acquire(); // Setting up relations often also involve calls to 'set', and we only want to enter this function once
1203
1248
  this._relations = {};
1204
1249
 
1205
- _.each( _.result( this, 'relations' ) || [], function( rel ) {
1250
+ _.each( this.relations || [], function( rel ) {
1206
1251
  Backbone.Relational.store.initializeRelation( this, rel, options );
1207
1252
  }, this );
1208
1253
 
@@ -1213,20 +1258,28 @@
1213
1258
 
1214
1259
  /**
1215
1260
  * When new values are set, notify this model's relations (also if options.silent is set).
1216
- * (Relation.setRelated locks this model before calling 'set' on it to prevent loops)
1261
+ * (called from `set`; Relation.setRelated locks this model before calling 'set' on it to prevent loops)
1262
+ * @param {Object} [changedAttrs]
1263
+ * @param {Object} [options]
1217
1264
  */
1218
- updateRelations: function( options ) {
1265
+ updateRelations: function( changedAttrs, options ) {
1219
1266
  if ( this._isInitialized && !this.isLocked() ) {
1220
1267
  _.each( this._relations, function( rel ) {
1221
- // Update from data in `rel.keySource` if data got set in there, or `rel.key` otherwise
1222
- var val = this.attributes[ rel.keySource ] || this.attributes[ rel.key ];
1223
- if ( rel.related !== val ) {
1224
- this.trigger( 'relational:change:' + rel.key, this, val, options || {} );
1268
+ if ( !changedAttrs || ( rel.keySource in changedAttrs || rel.key in changedAttrs ) ) {
1269
+ // Fetch data in `rel.keySource` if data got set in there, or `rel.key` otherwise
1270
+ var value = this.attributes[ rel.keySource ] || this.attributes[ rel.key ],
1271
+ attr = changedAttrs && ( changedAttrs[ rel.keySource ] || changedAttrs[ rel.key ] );
1272
+
1273
+ // Update a relation if its value differs from this model's attributes, or it's been explicitly nullified.
1274
+ // Which can also happen before the originally intended related model has been found (`val` is null).
1275
+ if ( rel.related !== value || ( value === null && attr === null ) ) {
1276
+ this.trigger( 'relational:change:' + rel.key, this, value, options || {} );
1277
+ }
1225
1278
  }
1226
1279
 
1227
1280
  // Explicitly clear 'keySource', to prevent a leaky abstraction if 'keySource' differs from 'key'.
1228
1281
  if ( rel.keySource !== rel.key ) {
1229
- delete rel.instance.attributes[ rel.keySource ];
1282
+ delete this.attributes[ rel.keySource ];
1230
1283
  }
1231
1284
  }, this );
1232
1285
  }
@@ -1250,7 +1303,7 @@
1250
1303
 
1251
1304
  /**
1252
1305
  * Get a specific relation.
1253
- * @param key {string} The relation key to look for.
1306
+ * @param {string} key The relation key to look for.
1254
1307
  * @return {Backbone.Relation} An instance of 'Backbone.Relation', if a relation was found for 'key', or null.
1255
1308
  */
1256
1309
  getRelation: function( key ) {
@@ -1267,23 +1320,24 @@
1267
1320
 
1268
1321
  /**
1269
1322
  * Retrieve related objects.
1270
- * @param key {string} The relation key to fetch models for.
1271
- * @param [options] {Object} Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
1272
- * @param [refresh=false] {boolean} Fetch existing models from the server as well (in order to update them).
1323
+ * @param {string} key The relation key to fetch models for.
1324
+ * @param {Object} [options] Options for 'Backbone.Model.fetch' and 'Backbone.sync'.
1325
+ * @param {Boolean} [refresh=false] Fetch existing models from the server as well (in order to update them).
1273
1326
  * @return {jQuery.when[]} An array of request objects
1274
1327
  */
1275
1328
  fetchRelated: function( key, options, refresh ) {
1276
1329
  // Set default `options` for fetch
1277
1330
  options = _.extend( { update: true, remove: false }, options );
1278
1331
 
1279
- var setUrl,
1332
+ var models,
1333
+ setUrl,
1280
1334
  requests = [],
1281
1335
  rel = this.getRelation( key ),
1282
1336
  idsToFetch = rel && ( ( rel.keyIds && rel.keyIds.slice( 0 ) ) || ( ( rel.keyId || rel.keyId === 0 ) ? [ rel.keyId ] : [] ) );
1283
1337
 
1284
1338
  // On `refresh`, add the ids for current models in the relation to `idsToFetch`
1285
1339
  if ( refresh ) {
1286
- var models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
1340
+ models = rel.related instanceof Backbone.Collection ? rel.related.models : [ rel.related ];
1287
1341
  _.each( models, function( model ) {
1288
1342
  if ( model.id || model.id === 0 ) {
1289
1343
  idsToFetch.push( model.id );
@@ -1293,20 +1347,20 @@
1293
1347
 
1294
1348
  if ( idsToFetch && idsToFetch.length ) {
1295
1349
  // Find (or create) a model for each one that is to be fetched
1296
- var created = [],
1297
- models = _.map( idsToFetch, function( id ) {
1298
- var model = Backbone.Relational.store.find( rel.relatedModel, id );
1299
-
1300
- if ( !model ) {
1301
- var attrs = {};
1302
- attrs[ rel.relatedModel.prototype.idAttribute ] = id;
1303
- model = rel.relatedModel.findOrCreate( attrs, options );
1304
- created.push( model );
1305
- }
1350
+ var created = [];
1351
+ models = _.map( idsToFetch, function( id ) {
1352
+ var model = rel.relatedModel.findModel( id );
1353
+
1354
+ if ( !model ) {
1355
+ var attrs = {};
1356
+ attrs[ rel.relatedModel.prototype.idAttribute ] = id;
1357
+ model = rel.relatedModel.findOrCreate( attrs, options );
1358
+ created.push( model );
1359
+ }
1360
+
1361
+ return model;
1362
+ }, this );
1306
1363
 
1307
- return model;
1308
- }, this );
1309
-
1310
1364
  // Try if the 'collection' can provide a url to fetch a set of models in one request.
1311
1365
  if ( rel.related instanceof Backbone.Collection && _.isFunction( rel.related.url ) ) {
1312
1366
  setUrl = rel.related.url( models );
@@ -1349,7 +1403,7 @@
1349
1403
  }, this );
1350
1404
  }
1351
1405
  }
1352
-
1406
+
1353
1407
  return requests;
1354
1408
  },
1355
1409
 
@@ -1421,14 +1475,14 @@
1421
1475
  }
1422
1476
 
1423
1477
  if ( attributes ) {
1424
- this.updateRelations( options );
1478
+ this.updateRelations( attributes, options );
1425
1479
  }
1426
1480
  }
1427
1481
  finally {
1428
1482
  // Try to run the global queue holding external events
1429
1483
  Backbone.Relational.eventQueue.unblock();
1430
1484
  }
1431
-
1485
+
1432
1486
  return result;
1433
1487
  },
1434
1488
 
@@ -1486,6 +1540,10 @@
1486
1540
  }
1487
1541
  else if ( rel instanceof Backbone.HasOne ) {
1488
1542
  value = value || rel.keyId;
1543
+
1544
+ if ( !value && !_.isObject( rel.keyContents ) ) {
1545
+ value = rel.keyContents || null;
1546
+ }
1489
1547
  }
1490
1548
  }
1491
1549
  }
@@ -1519,7 +1577,7 @@
1519
1577
  delete json[ rel.key ];
1520
1578
  }
1521
1579
  });
1522
-
1580
+
1523
1581
  this.release();
1524
1582
  return json;
1525
1583
  }
@@ -1531,8 +1589,8 @@
1531
1589
  * @returns {Backbone.RelationalModel.constructor}
1532
1590
  */
1533
1591
  setup: function( superModel ) {
1534
- // We don't want to share a relations array with a parent, as this will cause problems with
1535
- // reverse relations.
1592
+ // We don't want to share a relations array with a parent, as this will cause problems with reverse
1593
+ // relations. Since `relations` may also be a property or function, only use slice if we have an array.
1536
1594
  this.prototype.relations = ( this.prototype.relations || [] ).slice( 0 );
1537
1595
 
1538
1596
  this._subModels = {};
@@ -1552,7 +1610,7 @@
1552
1610
  if ( !rel.model ) {
1553
1611
  rel.model = this;
1554
1612
  }
1555
-
1613
+
1556
1614
  if ( rel.reverseRelation && rel.model === this ) {
1557
1615
  var preInitialize = true;
1558
1616
  if ( _.isString( rel.relatedModel ) ) {
@@ -1577,7 +1635,7 @@
1577
1635
  }
1578
1636
  }
1579
1637
  }, this );
1580
-
1638
+
1581
1639
  return this;
1582
1640
  },
1583
1641
 
@@ -1592,8 +1650,8 @@
1592
1650
  this.initializeModelHierarchy();
1593
1651
 
1594
1652
  // Determine what type of (sub)model should be built if applicable.
1595
- var model = this._findSubModelType(this, attributes) || this;
1596
-
1653
+ var model = this._findSubModelType( this, attributes ) || this;
1654
+
1597
1655
  return new model( attributes, options );
1598
1656
  },
1599
1657
 
@@ -1606,16 +1664,17 @@
1606
1664
  * @param {Object} attributes
1607
1665
  * @return {Backbone.Model}
1608
1666
  */
1609
- _findSubModelType: function (type, attributes) {
1667
+ _findSubModelType: function( type, attributes ) {
1610
1668
  if ( type._subModels && type.prototype.subModelTypeAttribute in attributes ) {
1611
- var subModelTypeAttribute = attributes[type.prototype.subModelTypeAttribute];
1612
- var subModelType = type._subModels[subModelTypeAttribute];
1669
+ var subModelTypeAttribute = attributes[ type.prototype.subModelTypeAttribute ];
1670
+ var subModelType = type._subModels[ subModelTypeAttribute ];
1613
1671
  if ( subModelType ) {
1614
1672
  return subModelType;
1615
- } else {
1673
+ }
1674
+ else {
1616
1675
  // Recurse into subModelTypes to find a match
1617
1676
  for ( subModelTypeAttribute in type._subModels ) {
1618
- subModelType = this._findSubModelType(type._subModels[subModelTypeAttribute], attributes);
1677
+ subModelType = this._findSubModelType( type._subModels[ subModelTypeAttribute ], attributes );
1619
1678
  if ( subModelType ) {
1620
1679
  return subModelType;
1621
1680
  }
@@ -1631,46 +1690,46 @@
1631
1690
  initializeModelHierarchy: function() {
1632
1691
  // Inherit any relations that have been defined in the parent model.
1633
1692
  this.inheritRelations();
1634
-
1693
+
1635
1694
  // If we came here through 'build' for a model that has 'subModelTypes' then try to initialize the ones that
1636
1695
  // haven't been resolved yet.
1637
1696
  if ( this.prototype.subModelTypes ) {
1638
- var resolvedSubModels = _.keys(this._subModels);
1639
- var unresolvedSubModels = _.omit(this.prototype.subModelTypes, resolvedSubModels);
1697
+ var resolvedSubModels = _.keys( this._subModels );
1698
+ var unresolvedSubModels = _.omit( this.prototype.subModelTypes, resolvedSubModels );
1640
1699
  _.each( unresolvedSubModels, function( subModelTypeName ) {
1641
1700
  var subModelType = Backbone.Relational.store.getObjectByName( subModelTypeName );
1642
1701
  subModelType && subModelType.initializeModelHierarchy();
1643
1702
  });
1644
1703
  }
1645
1704
  },
1646
-
1705
+
1647
1706
  inheritRelations: function() {
1648
1707
  // Bail out if we've been here before.
1649
- if (!_.isUndefined( this._superModel ) && !_.isNull( this._superModel )) {
1650
- return;
1708
+ if (!_.isUndefined( this._superModel ) && !_.isNull( this._superModel )) {
1709
+ return;
1651
1710
  }
1652
1711
  // Try to initialize the _superModel.
1653
1712
  Backbone.Relational.store.setupSuperModel( this );
1654
-
1655
- // If a superModel has been found, copy relations from the _superModel if they haven't been inherited automatically
1713
+
1714
+ // If a superModel has been found, copy relations from the _superModel if they haven't been inherited automatically
1656
1715
  // (due to a redefinition of 'relations').
1657
1716
  if ( this._superModel ) {
1658
- // The _superModel needs a chance to initialize its own inherited relations before we attempt to inherit relations
1717
+ // The _superModel needs a chance to initialize its own inherited relations before we attempt to inherit relations
1659
1718
  // from the _superModel. You don't want to call 'initializeModelHierarchy' because that could cause sub-models of
1660
1719
  // this class to inherit their relations before this class has had chance to inherit it's relations.
1661
1720
  this._superModel.inheritRelations();
1662
1721
  if ( this._superModel.prototype.relations ) {
1663
1722
  // Find relations that exist on the '_superModel', but not yet on this model.
1664
- var inheritedRelations = _.select( this._superModel.prototype.relations || [], function( superRel ) {
1723
+ var inheritedRelations = _.filter( this._superModel.prototype.relations || [], function( superRel ) {
1665
1724
  return !_.any( this.prototype.relations || [], function( rel ) {
1666
1725
  return superRel.relatedModel === rel.relatedModel && superRel.key === rel.key;
1667
1726
  }, this );
1668
1727
  }, this );
1669
-
1728
+
1670
1729
  this.prototype.relations = inheritedRelations.concat( this.prototype.relations );
1671
1730
  }
1672
1731
  }
1673
- // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail the
1732
+ // Otherwise, make sure we don't get here again for this type by making '_superModel' false so we fail the
1674
1733
  // isUndefined/isNull check next time.
1675
1734
  else {
1676
1735
  this._superModel = false;
@@ -1679,9 +1738,9 @@
1679
1738
 
1680
1739
  /**
1681
1740
  * Find an instance of `this` type in 'Backbone.Relational.store'.
1682
- * - If `attributes` is a string or a number, `findOrCreate` will just query the `store` and return a model if found.
1741
+ * A new model is created with `attributes` (unless `options.create` is explicitly set to `false`) if no match is found.
1742
+ * - If `attributes` is a string or a number, `findOrCreate` will query the `store` and return a model if found.
1683
1743
  * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
1684
- * Otherwise, a new model is created with `attributes` (unless `options.create` is explicitly set to `false`).
1685
1744
  * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
1686
1745
  * @param {Object} [options]
1687
1746
  * @param {Boolean} [options.create=true]
@@ -1694,8 +1753,9 @@
1694
1753
  var parsedAttributes = ( _.isObject( attributes ) && options.parse && this.prototype.parse ) ?
1695
1754
  this.prototype.parse( _.clone( attributes ) ) : attributes;
1696
1755
 
1697
- // Try to find an instance of 'this' model type in the store
1698
- var model = Backbone.Relational.store.find( this, parsedAttributes );
1756
+ // If specified, use a custom `find` function to match up existing models to the given attributes.
1757
+ // Otherwise, try to find an instance of 'this' model type in the store
1758
+ var model = this.findModel( parsedAttributes );
1699
1759
 
1700
1760
  // If we found an instance, update it with the data in 'item' (unless 'options.merge' is false).
1701
1761
  // If not, create an instance (unless 'options.create' is false).
@@ -1717,7 +1777,7 @@
1717
1777
 
1718
1778
  /**
1719
1779
  * Find an instance of `this` type in 'Backbone.Relational.store'.
1720
- * - If `attributes` is a string or a number, `find` will just query the `store` and return a model if found.
1780
+ * - If `attributes` is a string or a number, `find` will query the `store` and return a model if found.
1721
1781
  * - If `attributes` is an object and is found in the store, the model will be updated with `attributes` unless `options.update` is `false`.
1722
1782
  * @param {Object|String|Number} attributes Either a model's id, or the attributes used to create or update a model.
1723
1783
  * @param {Object} [options]
@@ -1729,6 +1789,16 @@
1729
1789
  options || ( options = {} );
1730
1790
  options.create = false;
1731
1791
  return this.findOrCreate( attributes, options );
1792
+ },
1793
+
1794
+ /**
1795
+ * A hook to override the matching when updating (or creating) a model.
1796
+ * The default implementation is to look up the model by id in the store.
1797
+ * @param {Object} attributes
1798
+ * @returns {Backbone.RelationalModel}
1799
+ */
1800
+ findModel: function( attributes ) {
1801
+ return Backbone.Relational.store.find( this, attributes );
1732
1802
  }
1733
1803
  });
1734
1804
  _.extend( Backbone.RelationalModel.prototype, Backbone.Semaphore );
@@ -1742,7 +1812,7 @@
1742
1812
  Backbone.Collection.prototype.__prepareModel = Backbone.Collection.prototype._prepareModel;
1743
1813
  Backbone.Collection.prototype._prepareModel = function ( attrs, options ) {
1744
1814
  var model;
1745
-
1815
+
1746
1816
  if ( attrs instanceof Backbone.Model ) {
1747
1817
  if ( !attrs.collection ) {
1748
1818
  attrs.collection = this;
@@ -1750,22 +1820,22 @@
1750
1820
  model = attrs;
1751
1821
  }
1752
1822
  else {
1753
- options || ( options = {} );
1823
+ options = options ? _.clone( options ) : {};
1754
1824
  options.collection = this;
1755
-
1825
+
1756
1826
  if ( typeof this.model.findOrCreate !== 'undefined' ) {
1757
1827
  model = this.model.findOrCreate( attrs, options );
1758
1828
  }
1759
1829
  else {
1760
1830
  model = new this.model( attrs, options );
1761
1831
  }
1762
-
1763
- if ( model && model.isNew() && !model._validate( attrs, options ) ) {
1832
+
1833
+ if ( model && model.validationError ) {
1764
1834
  this.trigger( 'invalid', this, attrs, options );
1765
1835
  model = false;
1766
1836
  }
1767
1837
  }
1768
-
1838
+
1769
1839
  return model;
1770
1840
  };
1771
1841
 
@@ -1785,9 +1855,7 @@
1785
1855
  models = this.parse( models, options );
1786
1856
  }
1787
1857
 
1788
- if ( !_.isArray( models ) ) {
1789
- models = models ? [ models ] : [];
1790
- }
1858
+ models = _.isArray( models ) ? models.slice() : ( models ? [ models ] : [] );
1791
1859
 
1792
1860
  var newModels = [],
1793
1861
  toAdd = [];
@@ -1814,7 +1882,7 @@
1814
1882
 
1815
1883
  // Add 'models' in a single batch, so the original add will only be called once (and thus 'sort', etc).
1816
1884
  // If `parse` was specified, the collection and contained models have been parsed now.
1817
- set.call( this, toAdd, _.defaults( { parse: false }, options ) );
1885
+ var result = set.call( this, toAdd, _.defaults( { parse: false }, options ) );
1818
1886
 
1819
1887
  _.each( newModels, function( model ) {
1820
1888
  // Fire a `relational:add` event for any model in `newModels` that has actually been added to the collection.
@@ -1822,8 +1890,8 @@
1822
1890
  this.trigger( 'relational:add', model, this, options );
1823
1891
  }
1824
1892
  }, this );
1825
-
1826
- return this;
1893
+
1894
+ return result;
1827
1895
  };
1828
1896
 
1829
1897
  /**
@@ -1836,7 +1904,7 @@
1836
1904
  return remove.apply( this, arguments );
1837
1905
  }
1838
1906
 
1839
- models = _.isArray( models ) ? models.slice( 0 ) : [ models ];
1907
+ models = _.isArray( models ) ? models.slice() : [ models ];
1840
1908
  options || ( options = {} );
1841
1909
 
1842
1910
  var toRemove = [];
@@ -1847,15 +1915,13 @@
1847
1915
  model && toRemove.push( model );
1848
1916
  }, this );
1849
1917
 
1850
- if ( toRemove.length ) {
1851
- remove.call( this, toRemove, options );
1918
+ var result = remove.call( this, toRemove, options );
1852
1919
 
1853
- _.each( toRemove, function( model ) {
1854
- this.trigger('relational:remove', model, this, options);
1855
- }, this );
1856
- }
1857
-
1858
- return this;
1920
+ _.each( toRemove, function( model ) {
1921
+ this.trigger('relational:remove', model, this, options);
1922
+ }, this );
1923
+
1924
+ return result;
1859
1925
  };
1860
1926
 
1861
1927
  /**
@@ -1864,13 +1930,13 @@
1864
1930
  var reset = Backbone.Collection.prototype.__reset = Backbone.Collection.prototype.reset;
1865
1931
  Backbone.Collection.prototype.reset = function( models, options ) {
1866
1932
  options = _.extend( { merge: true }, options );
1867
- reset.call( this, models, options );
1933
+ var result = reset.call( this, models, options );
1868
1934
 
1869
1935
  if ( this.model.prototype instanceof Backbone.RelationalModel ) {
1870
1936
  this.trigger( 'relational:reset', this, options );
1871
1937
  }
1872
1938
 
1873
- return this;
1939
+ return result;
1874
1940
  };
1875
1941
 
1876
1942
  /**
@@ -1878,13 +1944,13 @@
1878
1944
  */
1879
1945
  var sort = Backbone.Collection.prototype.__sort = Backbone.Collection.prototype.sort;
1880
1946
  Backbone.Collection.prototype.sort = function( options ) {
1881
- sort.call( this, options );
1947
+ var result = sort.call( this, options );
1882
1948
 
1883
1949
  if ( this.model.prototype instanceof Backbone.RelationalModel ) {
1884
1950
  this.trigger( 'relational:reset', this, options );
1885
1951
  }
1886
1952
 
1887
- return this;
1953
+ return result;
1888
1954
  };
1889
1955
 
1890
1956
  /**
@@ -1901,14 +1967,14 @@
1901
1967
  if ( eventName === 'add' || eventName === 'remove' || eventName === 'reset' || eventName === 'sort' ) {
1902
1968
  var dit = this,
1903
1969
  args = arguments;
1904
-
1970
+
1905
1971
  if ( _.isObject( args[ 3 ] ) ) {
1906
1972
  args = _.toArray( args );
1907
1973
  // the fourth argument is the option object.
1908
1974
  // we need to clone it, as it could be modified while we wait on the eventQueue to be unblocked
1909
1975
  args[ 3 ] = _.clone( args[ 3 ] );
1910
1976
  }
1911
-
1977
+
1912
1978
  Backbone.Relational.eventQueue.add( function() {
1913
1979
  trigger.apply( dit, args );
1914
1980
  });
@@ -1916,14 +1982,14 @@
1916
1982
  else {
1917
1983
  trigger.apply( this, arguments );
1918
1984
  }
1919
-
1985
+
1920
1986
  return this;
1921
1987
  };
1922
1988
 
1923
1989
  // Override .extend() to automatically call .setup()
1924
1990
  Backbone.RelationalModel.extend = function( protoProps, classProps ) {
1925
1991
  var child = Backbone.Model.extend.apply( this, arguments );
1926
-
1992
+
1927
1993
  child.setup( this );
1928
1994
 
1929
1995
  return child;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backbone-relational-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.8.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Marsh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-19 00:00:00.000000000 Z
11
+ date: 2014-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties