Skip to content

Commit 33bf327

Browse files
committed
now supports hasOne associations
still TODO, auto parsing when fetching a model
1 parent 4a3bc7d commit 33bf327

File tree

3 files changed

+136
-66
lines changed

3 files changed

+136
-66
lines changed

spec/rails_model.spec.js

Lines changed: 92 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ describe("RailsModel", function() {
2020
expect(book.attributes).toBeDefined();
2121
});
2222

23-
describe("hasMany associations", function() {
23+
describe("Associations Using Nested Attributes", function() {
2424

2525
beforeEach(function() {
26-
Book = Backbone.RailsModel.extend(hasManyNested);
26+
Book = Backbone.RailsModel.extend(nestedAssociations);
2727
});
2828

2929
describe("Empty model", function() {
@@ -35,34 +35,29 @@ describe("RailsModel", function() {
3535
expect(book.pages instanceof Collections.Pages).toBeTruthy();
3636
expect(book.customerReviews instanceof Collections.CustomerReviews).toBeTruthy();
3737
});
38+
39+
it("should have models set", function(){
40+
expect(book.author instanceof Models.Author).toBeTruthy();
41+
expect(book.publishingHouse instanceof Models.PublishingHouse).toBeTruthy();
42+
});
3843

3944
describe("serialization", function() {
40-
it("should not contain the books and reviews collections", function() {
45+
it("should not contain the collections", function() {
4146
expect(book.toJSON().pages_attributes).toBeUndefined();
4247
expect(book.toJSON().customer_reviews_attributes).toBeUndefined();
4348
});
4449

45-
describe("asNestedAttributes: false", function(){
46-
beforeEach(function(){
47-
Book = Backbone.RailsModel.extend(hasManyNotNested);
48-
book = new Book();
49-
});
50-
51-
it("should not contain the books and reviews collection", function() {
52-
expect(book.toJSON().pages).toBeUndefined();
53-
expect(book.toJSON().customer_reviews).toBeUndefined();
54-
});
50+
it("should not contain the models", function() {
51+
expect(book.toJSON().author_attributes).toBeUndefined();
52+
expect(book.toJSON().publishing_house_attributes).toBeUndefined();
5553
});
5654
});
5755

5856
});
5957

6058
describe("Populated model", function() {
6159
beforeEach(function() {
62-
book = new Book({
63-
pages: [{one:1}, {two:2}],
64-
customer_reviews: [{one:'great'},{two:'mediocre'},{three:'soso'}]
65-
});
60+
book = new Book(sampleBook);
6661
});
6762

6863
it("should have a populated each collection", function() {
@@ -75,11 +70,21 @@ describe("RailsModel", function() {
7570
expect(book.customerReviews.at(1).get('two')).toEqual('mediocre');
7671
expect(book.customerReviews.at(2).get('three')).toEqual('soso');
7772
});
73+
74+
it("should have populated each model", function(){
75+
expect(book.author.get('name')).toEqual('John Doe');
76+
expect(book.publishingHouse.get('name')).toEqual('Books co.');
77+
});
7878

79-
it("should not have the collection set as attributes", function(){
79+
it("should not have the passed in collection names set as attributes", function(){
8080
expect(book.get('pages')).toBeUndefined();
8181
expect(book.get('customer_reviews')).toBeUndefined();
8282
});
83+
84+
it("should not have the passed in model names set as attributes", function(){
85+
expect(book.get('author')).toBeUndefined();
86+
expect(book.get('publishing_house')).toBeUndefined();
87+
});
8388

8489
describe("serialization", function() {
8590
it("should have both collection's attributes", function() {
@@ -89,34 +94,81 @@ describe("RailsModel", function() {
8994
expect(book.toJSON().customer_reviews_attributes).toBeDefined();
9095
expect(book.toJSON().customer_reviews_attributes[0].one).toEqual('great');
9196
});
97+
98+
it("should have model attributes set", function(){
99+
expect(book.toJSON().author_attributes).toBeDefined();
100+
expect(book.toJSON().author_attributes.name).toEqual('John Doe');
101+
102+
expect(book.toJSON().publishing_house_attributes).toBeDefined();
103+
expect(book.toJSON().publishing_house_attributes.name).toEqual('Books co.');
104+
});
92105

93106
it("should not have pages or customer_reviews", function() {
94107
expect(book.toJSON().pages).toBeUndefined();
95108
expect(book.toJSON().customer_reviews).toBeUndefined();
96109
});
110+
111+
it("should not have author or publishing_house", function() {
112+
expect(book.toJSON().author).toBeUndefined();
113+
expect(book.toJSON().publishing_house).toBeUndefined();
114+
});
115+
});
116+
});
117+
});
118+
119+
describe("Associations Without Using Nested Attributes", function() {
120+
121+
beforeEach(function() {
122+
Book = Backbone.RailsModel.extend(nonNestedAssociations);
123+
});
97124

98-
describe("asNestedAttributes: false", function() {
99-
beforeEach(function() {
100-
Book = Backbone.RailsModel.extend(hasManyNotNested);
101-
102-
book = new Book({
103-
pages: [{one:1}, {two:2}],
104-
customer_reviews: [{one:'great'},{two:'mediocre'},{three:'soso'}]
105-
});
106-
});
107-
108-
it("should have pages", function(){
109-
expect(book.toJSON().pages).toBeDefined();
110-
expect(book.toJSON().pages[0].one).toEqual(1);
111-
112-
expect(book.toJSON().customer_reviews).toBeDefined();
113-
expect(book.toJSON().customer_reviews[0].one).toEqual('great');
114-
});
115-
116-
it("should not have pages_attributes", function() {
117-
expect(book.toJSON().pages_attributes).toBeUndefined();
118-
expect(book.toJSON().customer_reviews_attributes).toBeUndefined();
119-
});
125+
describe("serialization", function(){
126+
127+
describe("empty model", function(){
128+
beforeEach(function(){
129+
book = new Book();
130+
});
131+
132+
it("should not include collections", function() {
133+
expect(book.toJSON().pages).toBeUndefined();
134+
expect(book.toJSON().customer_reviews).toBeUndefined();
135+
});
136+
137+
it("should not include models", function() {
138+
expect(book.toJSON().author).toBeUndefined();
139+
expect(book.toJSON().publishing_house).toBeUndefined();
140+
});
141+
});
142+
143+
describe("populated model", function() {
144+
beforeEach(function() {
145+
book = new Book(sampleBook);
146+
});
147+
148+
it("should include collections", function(){
149+
expect(book.toJSON().pages).toBeDefined();
150+
expect(book.toJSON().pages[0].one).toEqual(1);
151+
152+
expect(book.toJSON().customer_reviews).toBeDefined();
153+
expect(book.toJSON().customer_reviews[0].one).toEqual('great');
154+
});
155+
156+
it("should include models", function(){
157+
expect(book.toJSON().author).toBeDefined();
158+
expect(book.toJSON().author.name).toEqual('John Doe');
159+
160+
expect(book.toJSON().publishing_house).toBeDefined();
161+
expect(book.toJSON().publishing_house.name).toEqual('Books co.');
162+
});
163+
164+
it("should not have _attributes collections", function() {
165+
expect(book.toJSON().pages_attributes).toBeUndefined();
166+
expect(book.toJSON().customer_reviews_attributes).toBeUndefined();
167+
});
168+
169+
it("should not have _attributes models", function() {
170+
expect(book.toJSON().author_attributes).toBeUndefined();
171+
expect(book.toJSON().publishing_house_attributes).toBeUndefined();
120172
});
121173
});
122174
});

spec/spec_helper.js

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
var Models = {},
22
Collections = {},
3-
hasManyNested,
4-
hasOneNested,
5-
hasOneAndManyNested,
6-
hasManyNotNested,
7-
hasOneNotNested,
8-
hasOneAndManyNotNested;
3+
nestedAssociations,
4+
nonNestedAssociations,
5+
sampleBook = {
6+
pages: [{one:1}, {two:2}],
7+
customer_reviews: [{one:'great'},{two:'mediocre'},{three:'soso'}],
8+
author: {name:'John Doe', phone: '123-456-7890'},
9+
publishing_house: {name: 'Books co.', phone: '012-345-6789'}
10+
};
911

1012
beforeEach(function(){
1113
Models.Author = Backbone.Model.extend();
@@ -14,18 +16,15 @@ beforeEach(function(){
1416
Collections.Pages = Backbone.RailsNestedAttributesCollection.extend();
1517
Collections.CustomerReviews = Backbone.RailsNestedAttributesCollection.extend();
1618

17-
hasManyNested = {
19+
nestedAssociations = {
1820
hasMany: {
1921
pages: {
2022
collection: Collections.Pages
2123
},
2224
customerReviews: {
2325
collection: Collections.CustomerReviews
2426
}
25-
}
26-
};
27-
28-
hasOneNested = {
27+
},
2928
hasOne: {
3029
author: {
3130
model: Models.Author
@@ -36,9 +35,7 @@ beforeEach(function(){
3635
}
3736
};
3837

39-
hasOneAndManyNested = _.extend(hasManyNested, hasOneNested);
40-
41-
hasManyNotNested = {
38+
nonNestedAssociations = {
4239
hasMany: {
4340
pages: {
4441
collection: Collections.Pages,
@@ -48,10 +45,7 @@ beforeEach(function(){
4845
collection: Collections.CustomerReviews,
4946
asNestedAttributes: false
5047
}
51-
}
52-
};
53-
54-
hasOneNotNested = {
48+
},
5549
hasOne: {
5650
author: {
5751
model: Models.Author,
@@ -64,5 +58,4 @@ beforeEach(function(){
6458
}
6559
};
6660

67-
hasOneAndManyNotNested = _.extend(hasManyNotNested, hasOneNotNested);
6861
});

src/rails_model.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
*/
3232
(function(){
3333

34+
/**
35+
*
36+
* Some String utility functions to work with camelCase and underscore strings
37+
*
38+
*/
3439
var
3540
/**
3641
*
@@ -66,7 +71,24 @@
6671
replace(/([a-z\d])([A-Z]+)/g, '$1_$2').
6772
replace(/\-|\s+/g, '_').toLowerCase();
6873
};
69-
74+
75+
var
76+
/**
77+
* @private
78+
* @binding {Backbone.RailsModel}
79+
* @param {String} type The type of association (collection|model)
80+
* @param {Object} properties The properties for this particular association (ie. which model/collection to uset etc...)
81+
* @param {String} association The name of the association to set
82+
*
83+
* Creates the associated model and populates it with data
84+
* Note this is not set on attributes, but directly on the model itself
85+
*
86+
*/
87+
createAssociation = function(type, properties, association){
88+
var railsAssociation = underscored(association);
89+
this[association] = new properties[type](this.get( railsAssociation ));
90+
this.unset(railsAssociation, {silent: true});
91+
};
7092

7193
Backbone.RailsModel = Backbone.Model.extend({
7294

@@ -92,9 +114,11 @@
92114
Backbone.Model.prototype.constructor.apply(this, arguments);
93115

94116
_.each(this.hasMany, function(properties, association){
95-
var railsAssociation = underscored(association);
96-
self[association] = new properties.collection(self.get( railsAssociation ));
97-
self.unset(railsAssociation, {silent: true});
117+
createAssociation.call(self, 'collection', properties, association);
118+
});
119+
120+
_.each(this.hasOne, function(properties, association){
121+
createAssociation.call(self, 'model', properties, association);
98122
});
99123
},
100124

@@ -110,10 +134,11 @@
110134
var self = this,
111135
attrs = _.clone(this.attributes);
112136

113-
_.each(this.hasMany, function(properties, association){
114-
var associationKey = underscored(association) + (properties.asNestedAttributes === false ? "" : "_attributes");
137+
_.each(_.extend({}, this.hasOne, this.hasMany), function(properties, association){
115138

116-
if ( self[association].length ){
139+
// checking for either: length (if collection), or populated attributes (if model)
140+
if ( self[association].length || !_.isEmpty(self[association].attributes)){
141+
var associationKey = underscored(association) + (properties.asNestedAttributes === false ? "" : "_attributes");
117142
attrs[associationKey] = self[association].toJSON();
118143
}
119144
});

0 commit comments

Comments
 (0)