backbone-relational-rails 0.8.6 → 0.8.7

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