Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
brainfood
/
backbone-nested-models
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
3a21b2ec
authored
2014-04-07 13:53:37 -0500
by
Adam Heath
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
Add 100% test coverage, and fix the few bugs that were found.
1 parent
1b9359a4
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
427 additions
and
23 deletions
src/scripts/backbone-nested-models.js
test/specs/backbone-nested-models.spec.js
src/scripts/backbone-nested-models.js
View file @
3a21b2e
...
...
@@ -22,7 +22,7 @@ define(
var
validation
=
_
.
extend
({},
oldValidation
);
var
found
;
var
f
=
function
(
value
)
{
if
(
value
===
validateNestedValue
)
{
if
(
typeof
value
===
'object'
&&
value
.
fn
===
validateNestedValue
)
{
found
=
true
;
}
};
...
...
@@ -44,7 +44,7 @@ define(
found
=
false
;
_
.
each
(
validators
,
f
);
if
(
!
found
)
{
validators
.
push
(
validateNestedValue
);
validators
.
push
(
{
fn
:
validateNestedValue
}
);
}
}
}
...
...
@@ -57,21 +57,21 @@ define(
modelClass
.
prototype
[
methodName
]
=
function
()
{
var
oldValidation
=
updateValidation
(
this
);
try
{
if
(
originalMethod
)
{
return
originalMethod
.
apply
(
this
,
arguments
);
}
else
{
return
modelClass
.
__super__
[
methodName
].
apply
(
this
,
arguments
);
}
return
originalMethod
.
apply
(
this
,
arguments
);
}
finally
{
this
.
validation
=
oldValidation
;
}
};
return
modelClass
;
}
function
wrapSetFunction
(
modelClass
)
{
var
originalMethod
=
modelClass
.
prototype
.
set
;
modelClass
.
prototype
.
set
=
function
(
key
,
val
,
options
)
{
var
attr
,
attrs
,
curVal
,
nestedOptions
,
newVal
;
if
(
typeof
(
key
)
===
'undefined'
)
{
return
this
;
}
if
(
key
===
null
)
{
return
this
;
}
...
...
@@ -93,23 +93,15 @@ define(
}
}
}
if
(
originalMethod
)
{
return
originalMethod
.
call
(
this
,
attrs
,
options
);
}
else
{
return
modelClass
.
__super__
.
set
.
call
(
this
,
attrs
,
options
);
}
return
originalMethod
.
call
(
this
,
attrs
,
options
);
};
return
modelClass
;
}
function
wrapToJSONFunction
(
modelClass
)
{
var
originalMethod
=
modelClass
.
prototype
.
toJSON
;
modelClass
.
prototype
.
toJSON
=
function
(
options
)
{
var
result
;
if
(
originalMethod
)
{
result
=
originalMethod
.
apply
(
this
,
arguments
);
}
else
{
result
=
modelClass
.
__super__
.
toJSON
.
apply
(
this
,
arguments
);
}
var
result
=
originalMethod
.
apply
(
this
,
arguments
);
if
(
options
&&
options
.
deep
)
{
_
.
each
(
result
,
function
(
value
,
key
)
{
if
(
value
instanceof
Backbone
.
Model
)
{
...
...
@@ -119,6 +111,7 @@ define(
}
return
result
;
};
return
modelClass
;
}
var
NestedModels
=
{
...
...
@@ -128,12 +121,17 @@ define(
wrapToJSONFunction
:
wrapToJSONFunction
,
wrapValidationFunction
:
wrapValidationFunction
,
wrapValidationFunctions
:
function
(
modelClass
)
{
modelClass
=
wrapValidationFunction
(
modelClass
,
'isValid'
);
modelClass
=
wrapValidationFunction
(
modelClass
,
'validate'
);
modelClass
=
wrapValidationFunction
(
modelClass
,
'preValidate'
);
return
modelClass
;
},
mixin
:
function
(
modelClass
)
{
wrapSetFunction
(
modelClass
);
wrapToJSONFunction
(
modelClass
);
wrapValidationFunction
(
modelClass
,
'isValid'
);
wrapValidationFunction
(
modelClass
,
'validate'
);
wrapValidationFunction
(
modelClass
,
'preValidate'
);
modelClass
=
NestedModels
.
wrapSetFunction
(
modelClass
);
modelClass
=
NestedModels
.
wrapToJSONFunction
(
modelClass
);
modelClass
=
NestedModels
.
wrapValidationFunctions
(
modelClass
);
return
modelClass
;
},
};
...
...
test/specs/backbone-nested-models.spec.js
0 → 100644
View file @
3a21b2e
define
(
function
(
require
)
{
'use strict'
;
var
NestedModels
=
require
(
'backbone-nested-models'
);
var
_
=
require
(
'underscore'
);
var
Backbone
=
require
(
'backbone'
);
require
(
'backbone-validation'
);
describe
(
'NestedModels'
,
function
()
{
it
(
'exists'
,
function
()
{
expect
(
NestedModels
).
toBeTruthy
();
});
});
describe
(
'NestedModels'
,
function
()
{
var
Top
,
TopWithMethods
,
Nested
;
beforeEach
(
function
()
{
Nested
=
Backbone
.
Model
.
extend
({
defaults
:
{
address1
:
null
,
countryGeoId
:
null
,
},
validation
:
{
address1
:
[
{
msg
:
'ADDRESS1 is REQUIRED'
,
required
:
true
},
],
countryGeoId
:
[
{
msg
:
'countryGeoId is not in the given set'
,
oneOf
:
[
'USA'
,
'CAN'
,
'RUS'
]},
],
},
});
Top
=
Backbone
.
Model
.
extend
({
defaults
:
function
()
{
return
{
nested
:
new
Nested
(),
name
:
null
,
};
},
validation
:
{
name
:
[
{
msg
:
'name IS required'
,
required
:
true
},
],
},
});
_
.
extend
(
Backbone
.
Model
.
prototype
,
Backbone
.
Validation
.
mixin
);
function
incrCount
(
self
,
name
)
{
if
(
!
self
.
counts
)
{
self
.
counts
=
{};
}
if
(
!
self
.
counts
[
name
])
{
self
.
counts
[
name
]
=
0
;
}
self
.
counts
[
name
]
++
;
}
TopWithMethods
=
Backbone
.
Model
.
extend
({
defaults
:
function
()
{
return
{
nested
:
new
Nested
(),
name
:
null
,
};
},
validation
:
{
name
:
[
{
msg
:
'name IS required'
,
required
:
true
},
],
},
set
:
function
()
{
incrCount
(
this
,
'set'
);
return
TopWithMethods
.
__super__
.
set
.
apply
(
this
,
arguments
);
},
toJSON
:
function
()
{
incrCount
(
this
,
'toJSON'
);
return
TopWithMethods
.
__super__
.
toJSON
.
apply
(
this
,
arguments
);
},
validate
:
function
()
{
incrCount
(
this
,
'validate'
);
return
TopWithMethods
.
__super__
.
validate
.
apply
(
this
,
arguments
);
},
preValidate
:
function
()
{
incrCount
(
this
,
'preValidate'
);
return
TopWithMethods
.
__super__
.
preValidate
.
apply
(
this
,
arguments
);
},
isValid
:
function
()
{
incrCount
(
this
,
'isValid'
);
return
TopWithMethods
.
__super__
.
isValid
.
apply
(
this
,
arguments
);
},
});
});
function
testSet
(
Top
,
checkCounts
)
{
function
extractCids
(
top
)
{
return
{
top
:
top
.
cid
,
nested
:
top
.
get
(
'nested'
).
cid
,
};
}
var
result
,
top
;
top
=
new
Top
();
var
cidsOriginal
=
extractCids
(
top
);
expect
(
cidsOriginal
.
top
).
not
.
toEqual
(
cidsOriginal
.
nested
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
();
expect
(
result
).
toBe
(
top
);
var
cidsNoargs
=
extractCids
(
top
);
expect
(
cidsOriginal
.
top
).
toEqual
(
cidsNoargs
.
top
);
expect
(
cidsOriginal
.
nested
).
toEqual
(
cidsNoargs
.
nested
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
(
null
);
expect
(
result
).
toBe
(
top
);
var
cidsNullkey
=
extractCids
(
top
);
expect
(
cidsOriginal
.
top
).
toEqual
(
cidsNullkey
.
top
);
expect
(
cidsOriginal
.
nested
).
toEqual
(
cidsNullkey
.
nested
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
({
name
:
'Name'
,
nested
:
new
Nested
({
address1
:
'1234 Main St.'
})});
expect
(
result
).
toBe
(
top
);
var
cidsNomerge
=
extractCids
(
top
);
expect
(
cidsOriginal
.
top
).
toEqual
(
cidsNomerge
.
top
);
expect
(
cidsOriginal
.
nested
).
not
.
toEqual
(
cidsNomerge
.
nested
);
expect
(
top
.
get
(
'name'
)).
toEqual
(
'Name'
);
expect
(
top
.
get
(
'nested'
).
get
(
'address1'
)).
toEqual
(
'1234 Main St.'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
2
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
({
name
:
'NAME'
,
nested
:
new
Nested
({
address1
:
'4321 Main St.'
})},
{
merge
:
true
});
expect
(
result
).
toBe
(
top
);
var
cidsMerge
=
extractCids
(
top
);
expect
(
cidsNomerge
.
top
).
toEqual
(
cidsMerge
.
top
);
expect
(
cidsNomerge
.
nested
).
toEqual
(
cidsMerge
.
nested
);
expect
(
top
.
get
(
'name'
)).
toEqual
(
'NAME'
);
expect
(
top
.
get
(
'nested'
).
get
(
'address1'
)).
toEqual
(
'4321 Main St.'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
3
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
(
'name'
,
'value'
);
expect
(
result
).
toBe
(
top
);
var
cidsSetname
=
extractCids
(
top
);
expect
(
cidsNomerge
.
top
).
toEqual
(
cidsSetname
.
top
);
expect
(
cidsNomerge
.
nested
).
toEqual
(
cidsSetname
.
nested
);
expect
(
top
.
get
(
'name'
)).
toEqual
(
'value'
);
expect
(
top
.
get
(
'nested'
).
get
(
'address1'
)).
toEqual
(
'4321 Main St.'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
4
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
(
'nested'
,
new
Nested
({
address1
:
'1234 Main St.'
}));
expect
(
result
).
toBe
(
top
);
var
cidsSetnested
=
extractCids
(
top
);
expect
(
cidsSetname
.
top
).
toEqual
(
cidsSetnested
.
top
);
expect
(
cidsSetname
.
nested
).
not
.
toEqual
(
cidsSetnested
.
nested
);
expect
(
top
.
get
(
'name'
)).
toEqual
(
'value'
);
expect
(
top
.
get
(
'nested'
).
get
(
'address1'
)).
toEqual
(
'1234 Main St.'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
5
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
set
(
'nested'
,
new
Nested
({
address1
:
'4321 Main St.'
}),
{
merge
:
true
});
expect
(
result
).
toBe
(
top
);
var
cidsSetnestedmerge
=
extractCids
(
top
);
expect
(
cidsSetnested
.
top
).
toEqual
(
cidsSetnestedmerge
.
top
);
expect
(
cidsSetnested
.
nested
).
toEqual
(
cidsSetnestedmerge
.
nested
);
expect
(
top
.
get
(
'name'
)).
toEqual
(
'value'
);
expect
(
top
.
get
(
'nested'
).
get
(
'address1'
)).
toEqual
(
'4321 Main St.'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
6
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
}
it
(
'set-mixin-plain'
,
function
()
{
testSet
.
call
(
this
,
NestedModels
.
mixin
(
Top
),
false
);
});
it
(
'set-mixin-methods'
,
function
()
{
testSet
.
call
(
this
,
NestedModels
.
mixin
(
TopWithMethods
),
true
);
});
it
(
'set-wrap-plain'
,
function
()
{
expect
(
NestedModels
.
wrapSetFunction
(
Top
)).
toBe
(
Top
);
testSet
.
call
(
this
,
Top
,
false
);
});
it
(
'set-wrap-methods'
,
function
()
{
expect
(
NestedModels
.
wrapSetFunction
(
TopWithMethods
)).
toBe
(
TopWithMethods
);
testSet
.
call
(
this
,
TopWithMethods
,
true
);
});
function
testToJSON
(
Top
,
checkCounts
)
{
var
result
,
top
,
rKeys
;
top
=
new
Top
();
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
toJSON
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toBeNull
();
expect
(
result
.
nested
).
not
.
toBeNull
();
expect
(
result
.
nested
).
toEqual
(
jasmine
.
any
(
Backbone
.
Model
));
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
toJSON
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
toJSON
({
deep
:
true
});
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toBeNull
();
expect
(
result
.
nested
).
not
.
toBeNull
();
expect
(
result
.
nested
).
not
.
toEqual
(
jasmine
.
any
(
Backbone
.
Model
));
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
toJSON
:
2
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
}
it
(
'toJSON-mixin-plain'
,
function
()
{
testToJSON
.
call
(
this
,
NestedModels
.
mixin
(
Top
),
false
);
});
it
(
'toJSON-mixin-methods'
,
function
()
{
testToJSON
.
call
(
this
,
NestedModels
.
mixin
(
TopWithMethods
),
true
);
});
it
(
'toJSON-wrap-plain'
,
function
()
{
expect
(
NestedModels
.
wrapToJSONFunction
(
Top
)).
toBe
(
Top
);
testToJSON
.
call
(
this
,
Top
,
false
);
});
it
(
'toJSON-wrap-methods'
,
function
()
{
expect
(
NestedModels
.
wrapToJSONFunction
(
TopWithMethods
)).
toBe
(
TopWithMethods
);
testToJSON
.
call
(
this
,
TopWithMethods
,
true
);
});
function
testValidation
(
Top
,
checkCounts
)
{
var
result
,
top
,
rKeys
;
top
=
new
Top
();
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
expect
(
top
.
isValid
()).
toBeUndefined
();
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
jasmine
.
any
(
String
));
expect
(
result
.
nested
).
toEqual
(
jasmine
.
any
(
String
));
expect
(
top
.
isValid
()).
toBe
(
false
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
validation
.
nested
=
[
{
fn
:
function
()
{
return
true
;
},
msg
:
'return-true'
},
];
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
expect
(
result
.
nested
).
toEqual
(
'return-true'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
2
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
validation
.
nested
=
[
{
fn
:
function
()
{
return
false
;
},
msg
:
'return-false'
},
];
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
3
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
validation
.
nested
=
[
{
fn
:
function
()
{
return
null
;
},
msg
:
'return-null'
},
];
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
expect
(
result
.
nested
).
toEqual
(
jasmine
.
any
(
String
));
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
4
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
get
(
'nested'
).
set
(
'address1'
,
'1234 Main St.'
);
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
expect
(
result
.
nested
).
toEqual
(
jasmine
.
any
(
String
));
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
5
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
get
(
'nested'
).
set
(
'countryGeoId'
,
'foobar'
);
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
expect
(
result
.
nested
).
toEqual
(
jasmine
.
any
(
String
));
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
6
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
get
(
'nested'
).
set
(
'countryGeoId'
,
'USA'
);
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
7
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
top
.
get
(
'nested'
).
clear
();
top
.
validation
.
nested
=
{
fn
:
NestedModels
.
validateNestedValue
,
msg
:
'installed-directly'
};
result
=
top
.
validate
();
expect
(
result
).
not
.
toBeNull
();
rKeys
=
_
.
keys
(
result
).
sort
();
expect
(
rKeys
).
toEqual
([
'name'
,
'nested'
]);
expect
(
result
.
name
).
toEqual
(
'name IS required'
);
expect
(
result
.
nested
).
toEqual
(
'installed-directly'
);
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
8
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
preValidate
(
'name'
,
null
);
expect
(
result
).
toEqual
(
jasmine
.
any
(
String
));
expect
(
top
.
get
(
'name'
)).
toBeNull
();
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
8
,
preValidate
:
1
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
result
=
top
.
preValidate
(
'name'
,
'NAME'
);
expect
(
result
).
toEqual
(
''
);
expect
(
top
.
get
(
'name'
)).
toBeNull
();
if
(
checkCounts
)
{
expect
(
top
.
counts
).
toEqual
({
set
:
1
,
isValid
:
2
,
validate
:
8
,
preValidate
:
2
});
}
else
{
expect
(
top
.
counts
).
toBeUndefined
();
}
}
it
(
'validation-mixin-plain'
,
function
()
{
testValidation
.
call
(
this
,
NestedModels
.
mixin
(
Top
),
false
);
});
it
(
'validation-mixin-methods'
,
function
()
{
testValidation
.
call
(
this
,
NestedModels
.
mixin
(
TopWithMethods
),
true
);
});
it
(
'validation-wrap-plain'
,
function
()
{
expect
(
NestedModels
.
wrapValidationFunctions
(
Top
)).
toBe
(
Top
);
testValidation
.
call
(
this
,
Top
,
false
);
});
it
(
'validation-mixin-methods'
,
function
()
{
expect
(
NestedModels
.
wrapValidationFunctions
(
TopWithMethods
)).
toBe
(
TopWithMethods
);
testValidation
.
call
(
this
,
TopWithMethods
,
true
);
});
});
});
Please
register
or
sign in
to post a comment