can-define
Advanced tools
Comparing version 0.7.29 to 0.8.0
@@ -37,6 +37,6 @@ "use strict"; | ||
var simpleEach = function(map, cb){ | ||
var eachPropertyDescriptor = function(map, cb){ | ||
for(var prop in map) { | ||
if(map.hasOwnProperty(prop)) { | ||
cb(map[prop], prop); | ||
cb(prop, Object.getOwnPropertyDescriptor(map,prop)); | ||
} | ||
@@ -388,3 +388,3 @@ } | ||
return function(newValue) { | ||
if (newValue instanceof Type) { | ||
if (newValue instanceof Type || newValue == null) { | ||
return set.call(this, newValue); | ||
@@ -524,3 +524,11 @@ } else { | ||
simpleEach(defines, function(value, prop) { | ||
eachPropertyDescriptor(defines, function( prop, propertyDescriptor ) { | ||
var value; | ||
if(propertyDescriptor.get || propertyDescriptor.set) { | ||
value = {get: propertyDescriptor.get, set: propertyDescriptor.set}; | ||
} else { | ||
value = propertyDescriptor.value; | ||
} | ||
if(prop === "constructor") { | ||
@@ -717,2 +725,5 @@ methods[prop] = value; | ||
'boolean': function(val) { | ||
if(val == null) { | ||
return val; | ||
} | ||
if (val === 'false' || val === '0' || !val) { | ||
@@ -719,0 +730,0 @@ return false; |
@@ -812,3 +812,3 @@ var QUnit = require("steal-qunit"); | ||
equal(t.boolean, false, "converted to boolean"); | ||
equal(t.boolean, undefined, "converted to boolean"); //Updated for canjs#2316 | ||
@@ -834,3 +834,3 @@ equal(t.htmlbool, false, "converted to htmlbool"); | ||
equal(t.boolean, false, "converted to boolean"); | ||
equal(t.boolean, null, "converted to boolean"); //Updated for canjs#2316 | ||
@@ -1227,1 +1227,87 @@ equal(t.htmlbool, false, "converted to htmlbool"); | ||
}); | ||
QUnit.test("nullish values are not converted for type or Type", function(assert) { | ||
var Foo = function() {}; | ||
var MyMap = define.Constructor({ | ||
map: { | ||
Type: Foo | ||
}, | ||
notype: {} | ||
}); | ||
var vm = new MyMap({ | ||
map: {}, | ||
notype: {} | ||
}); | ||
// Sanity check | ||
assert.ok(vm.map instanceof Foo, "map is another type"); | ||
assert.ok(vm.notype instanceof Object, "notype is an Object"); | ||
vm.map = null; | ||
vm.notype = null; | ||
assert.equal(vm.map, null, "map is null"); | ||
assert.equal(vm.map, null, "notype is null"); | ||
}); | ||
QUnit.test("shorthand getter (#56)", function(){ | ||
var Person = function(first, last) { | ||
this.first = first; | ||
this.last = last; | ||
}; | ||
define(Person.prototype, { | ||
first: "*", | ||
last: "*", | ||
get fullName() { | ||
return this.first + " " + this.last; | ||
} | ||
}); | ||
var p = new Person("Mohamed", "Cherif"); | ||
p.on("fullName", function(ev, newVal, oldVal) { | ||
QUnit.equal(oldVal, "Mohamed Cherif"); | ||
QUnit.equal(newVal, "Justin Meyer"); | ||
}); | ||
equal(p.fullName, "Mohamed Cherif", "fullName initialized right"); | ||
canBatch.start(); | ||
p.first = "Justin"; | ||
p.last = "Meyer"; | ||
canBatch.stop(); | ||
}); | ||
QUnit.test("shorthand getter setter (#56)", function(){ | ||
var Person = function(first, last) { | ||
this.first = first; | ||
this.last = last; | ||
}; | ||
define(Person.prototype, { | ||
first: "*", | ||
last: "*", | ||
get fullName() { | ||
return this.first + " " + this.last; | ||
}, | ||
set fullName(newVal){ | ||
var parts = newVal.split(" "); | ||
this.first = parts[0]; | ||
this.last = parts[1]; | ||
} | ||
}); | ||
var p = new Person("Mohamed", "Cherif"); | ||
p.on("fullName", function(ev, newVal, oldVal) { | ||
QUnit.equal(oldVal, "Mohamed Cherif"); | ||
QUnit.equal(newVal, "Justin Meyer"); | ||
}); | ||
equal(p.fullName, "Mohamed Cherif", "fullName initialized right"); | ||
p.fullName = "Justin Meyer"; | ||
}); |
@@ -5,4 +5,5 @@ @module {function} can-define | ||
and their behavior on a prototype object. | ||
@group can-define.static static | ||
@group can-define.typedefs types | ||
@group can-define.static 0 static | ||
@group can-define.typedefs 1 types | ||
@group can-define.behaviors 2 behaviors | ||
@@ -9,0 +10,0 @@ @signature `define(prototype, propDefinitions)` |
@@ -5,3 +5,3 @@ @property {Object} can-define.types types | ||
observable property. All type converters leave `null` and `undefined` as is except for | ||
the `"boolean"` type converter. | ||
the `"htmlbool"` type converter. | ||
@@ -8,0 +8,0 @@ @option {function} observable The default type behavior. It converts plain Objects to |
@function can-define.types.serialize serialize | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -4,0 +4,0 @@ Defines custom serialization behavior for a property. |
@typedef {function|string} can-define.types.type type | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -4,0 +4,0 @@ Converts a value set on an instance into an appropriate value. |
@typedef {function|can-define.types.propDefinition|Array} can-define.types.TypeConstructor Type | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -18,4 +18,3 @@ Provides a constructor function to be used to convert any set value into an appropriate | ||
`Type` is called before [can-define.types.type] and before [can-define.types.set]. It checks if the incoming value | ||
is an [instanceof](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) `Type`. If it is, | ||
it passed the original value through. If not, it passes the original value to `new Type(originalValue)` and returns the | ||
is an [instanceof](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) `Type`. If it is, or if it is `null` or `undefined`, it passes the original value through. If not, it passes the original value to `new Type(originalValue)` and returns the | ||
new instance to be set. | ||
@@ -22,0 +21,0 @@ |
@function can-define.types.get get | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -31,3 +31,3 @@ Specify what happens when a certain property is read on a map. `get` functions | ||
@signature `get( lastSetValue, setPropValue(value) )` | ||
@signature `get( lastSetValue, resolve(value) )` | ||
@@ -37,3 +37,3 @@ Asynchronously defines the behavior when a value is read on an instance. Used to provide property values that | ||
Only observed properties (via [can-event.on], [can-event.addEventListener], etc) will be passed the `setPropValue` function. It will be `undefined` if the value is not observed. This is for memory safety. | ||
Only observed properties (via [can-event.on], [can-event.addEventListener], etc) will be passed the `resolve` function. It will be `undefined` if the value is not observed. This is for memory safety. | ||
@@ -44,3 +44,3 @@ Specify `get` like: | ||
propertyName: { | ||
get: function(lastSetValue, setPropValue){ ... } | ||
get: function(lastSetValue, resolve){ ... } | ||
} | ||
@@ -51,6 +51,6 @@ ``` | ||
@param {function(*)|undefined} setPropValue(value) Updates the value of the property. This can be called | ||
multiple times if needed. | ||
@param {function|undefined} resolve(value) Updates the value of the property. This can be called | ||
multiple times if needed. Will be `undefined` if the value is not observed. | ||
@return {*} The value of the property before `setPropValue` is called. Or a value for unobserved property reads | ||
@return {*} The value of the property before `resolve` is called. Or a value for unobserved property reads | ||
to return. | ||
@@ -107,6 +107,6 @@ | ||
person: { | ||
get: function(lastSetValue, setPropValue){ | ||
get: function(lastSetValue, resolve){ | ||
Person.get({id: this.personId}) | ||
.then(function(person){ | ||
setPropValue(person); | ||
resolve(person); | ||
}); | ||
@@ -113,0 +113,0 @@ } |
@@ -1,7 +0,7 @@ | ||
@typedef {Object|String|Constructor} can-define.types.propDefinition propDefinition | ||
@typedef {Object|String|Constructor|Array|GETTER|SETTER} can-define.types.propDefinition PropDefinition | ||
@parent can-define.typedefs | ||
Defines the type, initial value, and get, set, and serialize behavior for an | ||
observable property. These behaviors can be specified with as an `Object`, `String` or | ||
`Constructor` function. | ||
observable property. These behaviors can be specified with as an `Object`, `String`, | ||
`Constructor` function, `Array`, a `getter expression`, or `setter expression`. | ||
@@ -143,3 +143,135 @@ @type {Object} Defines multiple behaviors for a single property. | ||
@type {Array} Defines an inline [can-define/list/list] Type setting. This is | ||
used as a shorthand for creating a property that is an [can-define/list/list] of another type. | ||
``` | ||
propertyName: [Constructor | propDefinitions] | ||
``` | ||
For example: | ||
```js | ||
users: [User], | ||
todos: [{complete: "boolean", name: "string"}] | ||
``` | ||
@type {GETTER} Defines a property's [can-define.types.get] behavior with the | ||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get syntax]. | ||
```js | ||
get propertyName(){ ... } | ||
``` | ||
For example: | ||
```js | ||
get fullName() { | ||
return this.first + " " + this.last; | ||
} | ||
``` | ||
This is a shorthand for providing an object with a `get` property like: | ||
``` | ||
fullName: { | ||
get: function(){ | ||
return this.first + " " + this.last; | ||
} | ||
} | ||
``` | ||
You must use an object with a [can-define.types.get] property if you want your get to take the `lastSetValue` | ||
or `resolve` arguments. | ||
@type {SETTER} Defines a property's [can-define.types.set] behavior with the | ||
[set syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set). | ||
```js | ||
set propertyName(newValue){ ... } | ||
``` | ||
For example: | ||
```js | ||
set fullName(newValue) { | ||
var parts = newVal.split(" "); | ||
this.first = parts[0]; | ||
this.last = parts[1]; | ||
} | ||
``` | ||
This is a shorthand for providing an object with a `set` property like: | ||
``` | ||
fullName: { | ||
set: function(newValue){ | ||
var parts = newVal.split(" "); | ||
this.first = parts[0]; | ||
this.last = parts[1]; | ||
} | ||
} | ||
``` | ||
You must use an object with a [can-define.types.set] property if you want your set to take the `resolve` argument. | ||
@body | ||
## Use | ||
A property definition can be defined in several ways. The `Object` form is the most literal | ||
and directly represents a `PropDefinition` object. The other forms | ||
get converted to a `PropDefinition` as follows: | ||
```js | ||
DefineMap.extend({ | ||
propertyA: Object -> PropertyDefinition | ||
propertyB: String -> {type: String} | ||
propertyC: Constructor -> {Type: Constructor} | ||
propertyD: [PropDefs] -> {Type: DefineList.extend({"*": PropDefs})>} | ||
get propertyE(){...} -> {get: propertyE(){...}} | ||
set propertyF(){...} -> {get: propertyF(){...}} | ||
method: Function | ||
}) | ||
``` | ||
Within a property definition, the available properties and their signatures look like: | ||
```js | ||
DefineMap.extend({ | ||
property: { | ||
get: function(lastSetValue, resolve){...}, | ||
set: function(newValue, resolve){...}, | ||
type: function(newValue, prop){...}| Array<PropertyDefinition> | PropertyDefinition, | ||
Type: Constructor | Array<PropertyDefinition> | PropertyDefinition, | ||
value: function(){...}, | ||
Value: Constructor, | ||
serialize: Boolean | function(){...} | ||
} | ||
}) | ||
``` | ||
For example: | ||
```js | ||
var Person = DefineMap.extend("Person",{ | ||
// a `DefineList` of `Address` | ||
addresses: [Address], | ||
// A `DefineMap` with a `first` and `last` property | ||
name: { type: {first: "string", last: "string"} }, | ||
// A `DefineList of a ``DefineMap` with a `make` and `year` property. | ||
cars: { Type: [{make: "string", year: "number"}] } | ||
}); | ||
var person = new Person({ | ||
addresses: [{street: "1134 Pinetree"}], | ||
name: {first: "Kath", last: "Iann"} | ||
cars: [{ make: "Nissan", year: 2010 }] | ||
}); | ||
``` |
@function can-define.types.set set | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
Specify what happens when a property value is set. | ||
@signature `set( [newVal,] [setValue] )` | ||
@signature `set( [newVal,] [resolve] )` | ||
@@ -34,3 +34,3 @@ A set function defines the behavior of what happens when a value is set on an | ||
@param {function(*)} [setValue(newValue)] A callback that can set the value of the property | ||
@param {function(*)} [resolve(newValue)] A callback that can set the value of the property | ||
asynchronously. | ||
@@ -47,4 +47,4 @@ | ||
- If the setter specifies the `newValue` argument only, the attribute value will be set to `undefined`. | ||
- If the setter specifies both `newValue` and `setValue`, the value of the property will not be | ||
updated until `setValue` is called. | ||
- If the setter specifies both `newValue` and `resolve`, the value of the property will not be | ||
updated until `resolve` is called. | ||
@@ -128,3 +128,3 @@ | ||
With 2 arguments, `undefined` leaves the property in place. It is expected | ||
that `setValue` will be called: | ||
that `resolve` will be called: | ||
@@ -134,3 +134,3 @@ ```js | ||
prop: { | ||
set: function(newVal, setValue){ | ||
set: function(newVal, resolve){ | ||
setVal(newVal+"d"); | ||
@@ -137,0 +137,0 @@ } |
@function can-define.types.value value | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -4,0 +4,0 @@ Returns the default value for instances of the defined type. The default value is defined on demand, when the property |
@function can-define.types.ValueConstructor Value | ||
@parent can-define.typedefs | ||
@parent can-define.behaviors | ||
@@ -4,0 +4,0 @@ Provides a constructor function to be used to provide a default value for a property. |
@@ -12,3 +12,3 @@ @typedef {Event} can-define/list/list/RemoveEvent remove | ||
``` | ||
list.on("removed", function(event, added, index){ ... }); | ||
list.on("remove", function(event, removed, index){ ... }); | ||
``` | ||
@@ -15,0 +15,0 @@ |
@@ -111,2 +111,44 @@ "use strict"; | ||
}); | ||
test('Concatenated list items Equal original', function() { | ||
var l = new DefineList([ | ||
{ firstProp: "Some data" }, | ||
{ secondProp: "Next data" } | ||
]), | ||
concatenated = l.concat([ | ||
{ hello: "World" }, | ||
{ foo: "Bar" } | ||
]); | ||
ok(l[0] === concatenated[0], "They are Equal"); | ||
ok(l[1] === concatenated[1], "They are Equal"); | ||
}); | ||
test('Lists with maps concatenate properly', function() { | ||
var Person = DefineMap.extend(); | ||
var People = DefineList.extend({ | ||
DefineMap: Person | ||
},{}); | ||
var Genius = Person.extend(); | ||
var Animal = DefineMap.extend(); | ||
var me = new Person({ name: "John" }); | ||
var animal = new Animal({ name: "Tak" }); | ||
var genius = new Genius({ name: "Einstein" }); | ||
var hero = { name: "Ghandi" }; | ||
var people = new People([]); | ||
var specialPeople = new People([ | ||
genius, | ||
hero | ||
]); | ||
people = people.concat([me, animal, specialPeople], specialPeople, [1, 2], 3); | ||
ok(people.length === 8, "List length is right"); | ||
ok(people[0] === me, "Map in list === vars created before concat"); | ||
ok(people[1] instanceof Person, "Animal got serialized to Person"); | ||
}); | ||
test('splice removes items in IE (#562)', function () { | ||
@@ -549,1 +591,29 @@ var l = new DefineList(['a']); | ||
}); | ||
QUnit.test("shorthand getter setter (#56)", function(){ | ||
var People = DefineList.extend({ | ||
first: "*", | ||
last: "*", | ||
get fullName() { | ||
return this.first + " " + this.last; | ||
}, | ||
set fullName(newVal){ | ||
var parts = newVal.split(" "); | ||
this.first = parts[0]; | ||
this.last = parts[1]; | ||
} | ||
}); | ||
var p = new People([]); | ||
p.fullName = "Mohamed Cherif"; | ||
p.on("fullName", function(ev, newVal, oldVal) { | ||
QUnit.equal(oldVal, "Mohamed Cherif"); | ||
QUnit.equal(newVal, "Justin Meyer"); | ||
}); | ||
equal(p.fullName, "Mohamed Cherif", "fullName initialized right"); | ||
p.fullName = "Justin Meyer"; | ||
}); |
@@ -19,2 +19,13 @@ var Construct = require("can-construct"); | ||
// Function that serializes the passed arg if | ||
// type does not match MapType of `this` list | ||
// then adds to args array | ||
var serializeNonTypes = function(MapType, arg, args) { | ||
if(arg && arg.serialize && !(arg instanceof MapType)) { | ||
args.push(new MapType(arg.serialize())); | ||
} else { | ||
args.push(arg); | ||
} | ||
}; | ||
var identity = function(x){ | ||
@@ -786,8 +797,28 @@ return x; | ||
*/ | ||
concat: function () { | ||
var args = []; | ||
each(makeArray(arguments), function (arg, i) { | ||
args[i] = arg instanceof DefineList ? arg.get() : arg; | ||
concat: function() { | ||
var args = [], | ||
MapType = this.constructor.DefineMap; | ||
// Go through each of the passed `arguments` and | ||
// see if it is list-like, an array, or something else | ||
each(arguments, function(arg) { | ||
if(types.isListLike(arg) || Array.isArray(arg)) { | ||
// If it is list-like we want convert to a JS array then | ||
// pass each item of the array to serializeNonTypes | ||
var arr = types.isListLike(arg) ? makeArray(arg) : arg; | ||
each(arr, function(innerArg) { | ||
serializeNonTypes(MapType, innerArg, args); | ||
}); | ||
} | ||
else { | ||
// If it is a Map, Object, or some primitive | ||
// just pass arg to serializeNonTypes | ||
serializeNonTypes(MapType, arg, args); | ||
} | ||
}); | ||
return new this.constructor(Array.prototype.concat.apply(this.get(), args)); | ||
// We will want to make `this` list into a JS array | ||
// as well (We know it should be list-like), then | ||
// concat with our passed in args, then pass it to | ||
// list constructor to make it back into a list | ||
return new this.constructor(Array.prototype.concat.apply(makeArray(this), args)); | ||
}, | ||
@@ -794,0 +825,0 @@ |
"use strict"; | ||
var QUnit = require("steal-qunit"); | ||
var define = require("can-define"); | ||
var DefineMap = require("can-define/map/map"); | ||
@@ -376,1 +375,28 @@ var Observation = require("can-observation"); | ||
}); | ||
QUnit.test("shorthand getter setter (#56)", function(){ | ||
var Person = DefineMap.extend({ | ||
first: "*", | ||
last: "*", | ||
get fullName() { | ||
return this.first + " " + this.last; | ||
}, | ||
set fullName(newVal){ | ||
var parts = newVal.split(" "); | ||
this.first = parts[0]; | ||
this.last = parts[1]; | ||
} | ||
}); | ||
var p = new Person({first: "Mohamed", last: "Cherif"}); | ||
p.on("fullName", function(ev, newVal, oldVal) { | ||
QUnit.equal(oldVal, "Mohamed Cherif"); | ||
QUnit.equal(newVal, "Justin Meyer"); | ||
}); | ||
equal(p.fullName, "Mohamed Cherif", "fullName initialized right"); | ||
p.fullName = "Justin Meyer"; | ||
}); |
{ | ||
"name": "can-define", | ||
"version": "0.7.29", | ||
"version": "0.8.0", | ||
"description": "Create observable objects with JS dot operator compatibility", | ||
@@ -36,9 +36,9 @@ "main": "can-define.js", | ||
"can-construct": "^3.0.0-pre.3", | ||
"can-event": "^3.0.0-pre.8", | ||
"can-event": "^3.0.0-pre.11", | ||
"can-observation": "^3.0.0-pre.11", | ||
"can-util": "^3.0.0-pre.35" | ||
"can-util": "^3.0.0-pre.50" | ||
}, | ||
"devDependencies": { | ||
"can-list": "^3.0.0-pre.6", | ||
"can-stache": "^3.0.0-pre.8", | ||
"can-list": "^3.0.0-pre.8", | ||
"can-stache": "^3.0.0-pre.20", | ||
"jshint": "^2.9.1", | ||
@@ -45,0 +45,0 @@ "serve": "^1.4.0", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
199500
4240
Updatedcan-event@^3.0.0-pre.11
Updatedcan-util@^3.0.0-pre.50