Skip to content

Commit 4a3bc7d

Browse files
committed
underscored and camelized syntax now supported
1 parent 4e9553c commit 4a3bc7d

File tree

4 files changed

+188
-42
lines changed

4 files changed

+188
-42
lines changed

SpecRunner.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<script type="text/javascript" src="lib/underscore.js"></script>
1313
<script type="text/javascript" src="lib/backbone.js"></script>
1414
<script type="text/javascript" src="lib/rails_nested_attributes_collection.js"></script>
15+
<script type="text/javascript" src="spec/spec_helper.js"></script>
1516

1617
<!-- include spec files here... -->
1718
<script type="text/javascript" src="spec/rails_model.spec.js"></script>

spec/rails_model.spec.js

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,56 @@
22
*
33
*
44
* Testing the RailsModel
5+
* See spec_helper.js for model and colleciton defn's
56
*
67
*/
78
describe("RailsModel", function() {
89
var book,
9-
Book,
10-
Pages = Backbone.RailsNestedAttributesCollection.extend();
11-
12-
beforeEach(function() {
13-
Book = Backbone.RailsModel.extend({
14-
hasMany: {
15-
pages: {
16-
collection: Pages
17-
}
18-
}
19-
});
20-
10+
Book;
11+
12+
beforeEach(function(){
13+
Book = Backbone.RailsModel.extend();
2114
});
2215

2316
// since we override the constructor
2417
it("should behave like a Backbone Model", function() {
2518
book = new Book();
19+
expect(book instanceof Backbone.Model).toBeTruthy();
2620
expect(book.attributes).toBeDefined();
2721
});
2822

2923
describe("hasMany associations", function() {
24+
25+
beforeEach(function() {
26+
Book = Backbone.RailsModel.extend(hasManyNested);
27+
});
3028

31-
describe("empty model", function() {
29+
describe("Empty model", function() {
3230
beforeEach(function() {
3331
book = new Book();
3432
});
3533

36-
it("should have a pages collection", function() {
37-
expect(book.pages instanceof Pages).toBeTruthy();
34+
it("should have collections set", function() {
35+
expect(book.pages instanceof Collections.Pages).toBeTruthy();
36+
expect(book.customerReviews instanceof Collections.CustomerReviews).toBeTruthy();
3837
});
3938

4039
describe("serialization", function() {
41-
it("should not contain the books collection", function() {
42-
// testing both nested attrs and not nested cases
43-
expect(book.toJSON().pages).toBeUndefined();
40+
it("should not contain the books and reviews collections", function() {
4441
expect(book.toJSON().pages_attributes).toBeUndefined();
42+
expect(book.toJSON().customer_reviews_attributes).toBeUndefined();
43+
});
44+
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+
});
4555
});
4656
});
4757

@@ -50,55 +60,62 @@ describe("RailsModel", function() {
5060
describe("Populated model", function() {
5161
beforeEach(function() {
5262
book = new Book({
53-
pages: [{one:1}, {two:2}]
63+
pages: [{one:1}, {two:2}],
64+
customer_reviews: [{one:'great'},{two:'mediocre'},{three:'soso'}]
5465
});
5566
});
5667

57-
it("should have a populated collection", function() {
68+
it("should have a populated each collection", function() {
5869
expect(book.pages.length).toEqual(2);
5970
expect(book.pages.at(0).get('one')).toEqual(1);
6071
expect(book.pages.at(1).get('two')).toEqual(2);
72+
73+
expect(book.customerReviews.length).toEqual(3);
74+
expect(book.customerReviews.at(0).get('one')).toEqual('great');
75+
expect(book.customerReviews.at(1).get('two')).toEqual('mediocre');
76+
expect(book.customerReviews.at(2).get('three')).toEqual('soso');
6177
});
6278

6379
it("should not have the collection set as attributes", function(){
6480
expect(book.get('pages')).toBeUndefined();
81+
expect(book.get('customer_reviews')).toBeUndefined();
6582
});
6683

6784
describe("serialization", function() {
68-
describe("using asNestedAttributes", function() {
69-
it("should have pages_attributes", function() {
70-
expect(book.toJSON().pages_attributes).toBeDefined();
71-
expect(book.toJSON().pages_attributes[0].one).toEqual(1);
72-
});
85+
it("should have both collection's attributes", function() {
86+
expect(book.toJSON().pages_attributes).toBeDefined();
87+
expect(book.toJSON().pages_attributes[0].one).toEqual(1);
88+
89+
expect(book.toJSON().customer_reviews_attributes).toBeDefined();
90+
expect(book.toJSON().customer_reviews_attributes[0].one).toEqual('great');
91+
});
7392

74-
it("should not have pages", function() {
75-
expect(book.toJSON().pages).toBeUndefined();
76-
});
93+
it("should not have pages or customer_reviews", function() {
94+
expect(book.toJSON().pages).toBeUndefined();
95+
expect(book.toJSON().customer_reviews).toBeUndefined();
7796
});
7897

79-
describe("not using asNestedAttributes", function() {
98+
describe("asNestedAttributes: false", function() {
8099
beforeEach(function() {
81-
Book = Backbone.RailsModel.extend({
82-
hasMany: {
83-
pages: {
84-
collection: Pages,
85-
asNestedAttributes: false
86-
}
87-
}
88-
});
100+
Book = Backbone.RailsModel.extend(hasManyNotNested);
89101

90102
book = new Book({
91-
pages: [{one:1}, {two:2}]
103+
pages: [{one:1}, {two:2}],
104+
customer_reviews: [{one:'great'},{two:'mediocre'},{three:'soso'}]
92105
});
93106
});
94107

95108
it("should have pages", function(){
96109
expect(book.toJSON().pages).toBeDefined();
97110
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');
98114
});
99115

100116
it("should not have pages_attributes", function() {
101117
expect(book.toJSON().pages_attributes).toBeUndefined();
118+
expect(book.toJSON().customer_reviews_attributes).toBeUndefined();
102119
});
103120
});
104121
});

spec/spec_helper.js

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,68 @@
1+
var Models = {},
2+
Collections = {},
3+
hasManyNested,
4+
hasOneNested,
5+
hasOneAndManyNested,
6+
hasManyNotNested,
7+
hasOneNotNested,
8+
hasOneAndManyNotNested;
19

2-
NestedCollection =
10+
beforeEach(function(){
11+
Models.Author = Backbone.Model.extend();
12+
Models.PublishingHouse = Backbone.Model.extend();
13+
14+
Collections.Pages = Backbone.RailsNestedAttributesCollection.extend();
15+
Collections.CustomerReviews = Backbone.RailsNestedAttributesCollection.extend();
16+
17+
hasManyNested = {
18+
hasMany: {
19+
pages: {
20+
collection: Collections.Pages
21+
},
22+
customerReviews: {
23+
collection: Collections.CustomerReviews
24+
}
25+
}
26+
};
27+
28+
hasOneNested = {
29+
hasOne: {
30+
author: {
31+
model: Models.Author
32+
},
33+
publishingHouse: {
34+
model: Models.PublishingHouse
35+
}
36+
}
37+
};
38+
39+
hasOneAndManyNested = _.extend(hasManyNested, hasOneNested);
40+
41+
hasManyNotNested = {
42+
hasMany: {
43+
pages: {
44+
collection: Collections.Pages,
45+
asNestedAttributes: false
46+
},
47+
customerReviews: {
48+
collection: Collections.CustomerReviews,
49+
asNestedAttributes: false
50+
}
51+
}
52+
};
53+
54+
hasOneNotNested = {
55+
hasOne: {
56+
author: {
57+
model: Models.Author,
58+
asNestedAttributes: false
59+
},
60+
publishingHouse: {
61+
model: Models.PublishingHouse,
62+
asNestedAttributes: false
63+
}
64+
}
65+
};
66+
67+
hasOneAndManyNotNested = _.extend(hasManyNotNested, hasOneNotNested);
68+
});

src/rails_model.js

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,69 @@
33
*
44
* A model that allows has_one and has_many associations
55
*
6+
* Associations are defined in the `class` definition using:
7+
*
8+
* var Book = Backbone.RailsModel.extend({
9+
* hasMany: {
10+
* pages: {
11+
* collection: Pages
12+
* },
13+
* customerReviews: {
14+
* collection: CustomerReviews
15+
* }
16+
* },
17+
* hasOne: {
18+
* publishingHouse: {
19+
* model: PublishingHouse
20+
* }
21+
* }
22+
* });
23+
*
24+
* Note that we specify our associations using the Javascript camelcased convention
25+
* The json coming in is expected to use the rails underscored convention however.
26+
*
27+
* For example, one would populate the above Book object using
28+
*
29+
* var book = new Book( {customer_reviews: [{one:1}, {two:2}]} )
30+
*
631
*/
732
(function(){
33+
34+
var
35+
/**
36+
*
37+
* @private
38+
*
39+
* Trim whitespace from the front and back of a string
40+
*/
41+
trim = String.prototype.trim || function(){
42+
return this.replace(/^\s+|\s+$/g, '') ;
43+
},
44+
/**
45+
*
46+
* @private
47+
* @param {String} string The string to be converted
48+
*
49+
* Convert underscored string into camelcase (taken from undescore.string.js)
50+
*/
51+
camelize = function(string){
52+
return trim.call(string).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
53+
return chr ? chr.toUpperCase() : '';
54+
});
55+
},
56+
/**
57+
*
58+
* @private
59+
* @param {String} string The string to be converted
60+
*
61+
* Convert camelcased string into underscored (taken from undescore.string.js)
62+
*/
63+
underscored = function(string){
64+
return trim.call(string).
65+
replace(/([A-Z\d]+)([A-Z][a-z])/, '$1_$2').
66+
replace(/([a-z\d])([A-Z]+)/g, '$1_$2').
67+
replace(/\-|\s+/g, '_').toLowerCase();
68+
};
869

970

1071
Backbone.RailsModel = Backbone.Model.extend({
@@ -31,8 +92,9 @@
3192
Backbone.Model.prototype.constructor.apply(this, arguments);
3293

3394
_.each(this.hasMany, function(properties, association){
34-
self[association] = new properties.collection(self.get(association));
35-
self.unset(association, {silent: true});
95+
var railsAssociation = underscored(association);
96+
self[association] = new properties.collection(self.get( railsAssociation ));
97+
self.unset(railsAssociation, {silent: true});
3698
});
3799
},
38100

@@ -49,7 +111,7 @@
49111
attrs = _.clone(this.attributes);
50112

51113
_.each(this.hasMany, function(properties, association){
52-
var associationKey = association + (properties.asNestedAttributes === false ? "" : "_attributes");
114+
var associationKey = underscored(association) + (properties.asNestedAttributes === false ? "" : "_attributes");
53115

54116
if ( self[association].length ){
55117
attrs[associationKey] = self[association].toJSON();

0 commit comments

Comments
 (0)