ampersand-state
Advanced tools
Comparing version 4.4.4 to 4.4.5
@@ -590,3 +590,5 @@ /*$AMPERSAND_VERSION*/ | ||
var newType; | ||
if (!_.isDate(newVal)) { | ||
if (newVal == null) { | ||
newType = typeof null; | ||
} else if (!_.isDate(newVal)) { | ||
try { | ||
@@ -608,2 +610,3 @@ var dateVal = new Date(newVal).valueOf(); | ||
} | ||
return { | ||
@@ -615,2 +618,3 @@ val: newVal, | ||
get: function (val) { | ||
if (val == null) { return val; } | ||
return new Date(val); | ||
@@ -617,0 +621,0 @@ }, |
{ | ||
"name": "ampersand-state", | ||
"description": "An observable, extensible state object with derived watchable properties.", | ||
"version": "4.4.4", | ||
"version": "4.4.5", | ||
"author": "Henrik Joreteg <henrik@andyet.net>", | ||
@@ -6,0 +6,0 @@ "browserify": { |
@@ -72,3 +72,3 @@ # ampersand-state | ||
If you have defined an **initialize** function for your subclass of State, it will be invoked at creation time. | ||
If you have defined an `initialize` function for your subclass of State, it will be invoked at creation time. | ||
@@ -91,3 +91,3 @@ ```javascript | ||
The attribute that should be used as the unique id of the state. `getId` uses this to determine the id for use when constructing a model's url for saving to the server. | ||
The attribute that should be used as the unique id of the state. `getId` uses this to determine the `id` for use when constructing a model's `url` for saving to the server. | ||
@@ -139,3 +139,3 @@ Defaults to `'id'`. | ||
Defaults to `ignore`. | ||
Defaults to `'ignore'`. | ||
@@ -191,3 +191,3 @@ ```javascript | ||
A special property of states, the **cid**, or a client id, is a unique identifier automatically assigned to all states when they are first created. Client ids are handy when the state has not been saved to the server, and so does not yet have its true **id** but needs a unique id so it can be rendered in the UI etc. | ||
A special property of states, the **`cid`**, or a client id, is a unique identifier automatically assigned to all states when they are first created. Client ids are handy when the state has not been saved to the server, and so does not yet have its true `id`, but needs a unique id (so it can be rendered in the UI, etc.). | ||
@@ -204,7 +204,7 @@ ```javascript | ||
Has this state been saved to the server yet? If the state does not yet have an id (using `getId()`), it is considered to be new. | ||
Has this state been saved to the server yet? If the state does not yet have an `id` (using `getId()`), it is considered to be new. | ||
### escape `state.escape()` | ||
Similar to `get`, but returns the HTML-escaped version of a state's attribute. If you're interpolating data from the state into HTML, using **escape** to retrieve attributes will help prevent XSS attacks. | ||
Similar to `get`, but returns the HTML-escaped version of a state's attribute. If you're interpolating data from the state into HTML, use `escape` when retrieving attributes to help prevent XSS attacks. | ||
@@ -221,3 +221,3 @@ ``` | ||
Check if the state is currently in a valid state, it does this by calling the `validate` method, of your state if you've provided one. | ||
Check if the state is currently valid. It does this by calling the state's `validate` method (if you've provided one). | ||
@@ -231,12 +231,9 @@ | ||
`set : function(newVal){}; returns {type : type, val : newVal};`: Called on every set, returns an object with two members : `val` and `type`. If the 'type' value does not equal the name of the dataType you defined, a `TypeError` will be thrown. | ||
`compare : function(currentVal, newVal, attributeName){}; returns boolean`: Called on every set, Should return true if `oldVal` and `newVal` are equal. Non-equal values will eventually trigger `change` events (unless `{silent : true}` is an option sent to the state's `set` method (not to be confused with the `set` method of the dataType). | ||
`get : function(val){} returns val;`: Overrides the default getter of this type. Useful if you want to make defensive copies. For example, the `date` dataType returns a clone of the internally saved `date` to keep the internal state consistent. | ||
`default : function(){} returns val;`: The returns the default value for this type. | ||
* `set : function(newVal){}; returns {type : type, val : newVal};`: Called on every set. Should return an object with two members: `val` and `type`. If the `type` value does not equal the name of the dataType you defined, a `TypeError` should be thrown. | ||
* `compare : function(currentVal, newVal, attributeName){}; returns boolean`: Called on every `set`. Should return `true` if `oldVal` and `newVal` are equal. Non-equal values will eventually trigger `change` events, unless the state's `set` (not the dataTypes's!) is called with the option `{silent : true}`. | ||
* `get : function(val){} returns val;`: Overrides the default getter of this type. Useful if you want to make defensive copies. For example, the `date` dataType returns a clone of the internally saved `date` to keep the internal state consistent. | ||
* `default : function(){} returns val;`: Returns the default value for this type. | ||
For example, let's say your application uses a special type of date, "JulianDate". You'd like to setup this as a type in state, but don't want to just use `any` or `object` as the type. To define it: | ||
For example, let's say your application uses a special type of date, `JulianDate`. You'd like to setup this as a type in state, but don't want to just use `any` or `object` as the type. To define it: | ||
```javascript | ||
@@ -308,19 +305,22 @@ // Julian Date is a 'class' defined elsewhere: | ||
Pass **props** as an object to extend, describing the observable properties of your state class. The props properties should not be set on an instance, as this won't define new properties, they should only be passed to extend. | ||
The **`props`** object describes the observable properties of your state class. Always pass `props` to `extend`; never set it on an instance, as it won't define new properties. | ||
Properties can be defined in three different ways: | ||
* As a string with the expected dataType. One of `string`, `number`, `boolean`, `array`, `object`, `date`, or `any`. Eg: `name: 'string'`. Can also be set to the name of a custom `dataTypes` if any are defined for the class. | ||
* As a string with the expected dataType. One of `string`, `number`, `boolean`, `array`, `object`, `date`, or `any`. (Example: `name: 'string'`.) Can also be set to the name of a custom `dataTypes`, if the class defines any. | ||
* An array of `[dataType, required, default]` | ||
* An object `{ type: 'string', required: true, default: '' , values: [], allowNull: false, setOnce: false }` | ||
* `default` will be the value that the property will be set to if it is undefined, either by not being set during initialization, or by being explicit set to undefined. | ||
* If `required` is true, one of two things will happen. If a `default` is set for the property, the property will start with that value, and revert to it after a call to `unset(propertyName)`. If a `default` is not set for the property, an error will be thrown after a call to `unset(propertyName)`. | ||
* `default` will be the value that the property will be set to if it is `undefined` (either by not being set during initialization, or by being explicit set to `undefined`). | ||
* If `required` is `true`, one of two things will happen | ||
* If the property has a `default`, it will start with that value, and revert to it after a call to `unset(propertyName)`. | ||
* If the property does not have a `default`, calls to `unset(propertyName)` will throw an error. | ||
* If `values` array is passed, then you'll be able to change a property to one of those values only. | ||
* If `setOnce` is true, then you'll be able to set property only once. | ||
* If used in combination with a `default` and you instantiate without that value, the default becomes the first value `set`. Thus cannot be set again. | ||
* If you don't have a default and don't set the value initially it can be set later, but only once. | ||
* Trying to set a property to an invalid type will raise an exception. | ||
* If `setOnce` is `true`, then you'll be able to set property only once. | ||
* If the property has a `default`, and you don't set the value initially, the property will be permanently set to the default value. | ||
* If the property doesn't have a `default`, and you don't set the value initially, it can be set later, but only once. | ||
* See [get](#ampersand-state-get) and [set](#ampersand-state-set) for more information about getting and setting properties. | ||
Trying to set a property to an invalid type will throw an error. | ||
See [get](#ampersand-state-get) and [set](#ampersand-state-set) for more information about getting and setting properties. | ||
```javascript | ||
@@ -342,5 +342,5 @@ var Person = AmpersandState.extend({ | ||
You will get an error if you try to set the default of any property as either an object or array. This is because those two data types are mutable and passed by reference. If you were to default a property to `[]` this would return *the same array* on every new instantiation of the state. | ||
You will get an error if you try to set the default of any property as either an object or array. This is because those two dataTypes are mutable and passed by reference. (Thus, if you *did* set a property's default to `['a','b']`, it would return *the same array* on every new instantiation of the state.) | ||
Instead, if you want to default a property to an array or object you can set `default` to a function like this | ||
Instead, if you want a property to default to an array or an object, just set `default` to a function, like this: | ||
@@ -358,3 +358,3 @@ ```javascript | ||
It's worth noting that both `array` and `object` do this already: they default to empty versions of themselves. You would only need to do this if you wanted to default to an array/object that wasn't empty. | ||
It's worth noting that both `array` and `object` have this behavior built-in: they default to empty versions of themselves. You would only need to do this if you wanted to default to an array/object that wasn't empty. | ||
@@ -378,3 +378,3 @@ ### session `AmpersandState.extend({ session: { name: 'string' } })` | ||
Derived properties (also known as computed properties) are properties of the state object that depend on the other (`props`, `session` or even `derived` properties to determine their value. Best demonstrated with an example: | ||
Derived properties (also known as computed properties) are properties of the state object that depend on other properties (from `props`, `session`, or even `derived`) to determine their value. Best demonstrated with an example: | ||
@@ -404,3 +404,3 @@ ```javascript | ||
Each derived property, is defined as an object with the current properties: | ||
Each derived property is defined as an object with the following properties: | ||
@@ -411,7 +411,7 @@ * `deps` {Array} - An array of property names which the derived property depends on. | ||
Derived properties are retrieved and fire change events just like any other property. They cannot be set directly. Caching ensures that the `fn` function is only run when any of the dependencies change, and change events are only fired if the result of calling `fn()` has actually changed. | ||
Derived properties are retrieved and fire `change` events just like any other property. However, they cannot be set directly. Caching ensures that the `fn` function is only run when any of the dependencies change, and `change` events are only fired if the result of calling `fn()` has actually changed. | ||
### children `AmpersandState.extend({ children: { profile: Profile } })` | ||
Define child state objects to attach to the object. Attributes passed to the constructor or to `set()` will be proxied to the children/collections. Change events on the child will be proxied through the parent: | ||
Define child state objects to attach to the object. Attributes passed to the constructor or to `set()` will be proxied to the children/collections. Childen's `change` events are proxied to the parent. | ||
@@ -454,2 +454,4 @@ ```javascript | ||
**Note:** Currently, events *don't* automatically proxy from collections to parent. This is for efficiency reasons. But there are ongoing discussions about how to best handle this. | ||
```javascript | ||
@@ -491,8 +493,6 @@ var State = require('ampersand-state'); | ||
Note that currently, events *don't* bubble from collections to parent automatically. This is done for efficiency reasons. But there are discussions ongoing about how to best handle this case. | ||
### parse | ||
**parse** is called when the state is initialized allowing the attributes to be modified/remapped/renamed/etc before they are actually applied to the state. In ampersand-state, parse is only called when the state is first initialized, and only if `{ parse: true }` is passed to the constructor options: | ||
**parse** is called when the state is initialized, allowing the attributes to be modified, remapped, renamed, etc., before they are actually applied to the state. In ampersand-state, `parse` is only called when the state is initialized, and *only* if `{ parse: true }` is passed to the constructor's options: | ||
@@ -541,3 +541,3 @@ ```javascript | ||
Set an attribute, or multiple attributes, on the state object. If any of the attributes change the state of the object, a `"change"` event will be triggered on it. Change events for specific attributes are also triggered, which you can listen to as well. For example: `"change:firstName"` and `"change:content"`. If the changes update any `derived` properties on the object, their values will be updated, and change events fired as well. | ||
Sets an attribute, or multiple attributes, on the state object. If any of the state object's attributes change, it will trigger a `"change"` event. Change events for specific attributes are also triggered, which you can listen to as well. For example: `"change:firstName"` and `"change:content"`. If the changes update any `derived` properties on the object, their values will be updated, and change events fired as well. | ||
@@ -567,7 +567,15 @@ Attributes can be set directly, or via a call to the backbone style `set` (useful if you wish to update multiple attributes at once): | ||
### toggle `state.toggle('active')` | ||
### clear `state.clear([options])` | ||
Shortcut to toggle boolean properties, or cycle through "ENUM" type properties that have a `values` array in its definition. Fires change events as you would expect from set. | ||
Clear all the attributes from the state object, by calling the `unset` function for each attribute, with the options provided. | ||
```javascript | ||
person.clear() | ||
``` | ||
### toggle `state.toggle('a')` | ||
Shortcut to toggle boolean properties, or cycle through “ENUM” type properties with a `values` array in their definition. Fires `"change"` events, as you would expect from `set()`. | ||
```javascript | ||
var Person = AmpersandState.extend({ | ||
@@ -607,3 +615,3 @@ props: { | ||
Return an object containing all the attributes that have changed, or false if there are no changed attributes. Useful for determining what parts of a view need to be updated and/or what attributes need to be persisted to the server. Unset attributes will be set to undefined. You can also pass an attributes object to diff against the state, determining if there *would be* a change. | ||
Return an object containing all the attributes that have changed, or `false` if there are no changed attributes. Useful for determining what parts of a view need to be updated and/or what attributes need to be persisted to the server. Unset attributes will be set to undefined. You can also pass an attributes object to diff against the state, determining if there *would be* a change. | ||
@@ -614,5 +622,5 @@ Note that if passing an attributes object to diff against, only changes to properties defined on the model will be detected. This means that changes to children or collections will *not* be returned as changes by this method. | ||
Return a shallow copy of the state's attributes for JSON stringification. This can be used for persistence, serialization, or for augmentation before being sent to the server. The name of this method is a bit confusing, as it doesn't actually return a JSON string — but I'm afraid that it's the way that the JavaScript API for JSON.stringify works. | ||
Return a shallow copy of the state's attributes for JSON stringification. This can be used for persistence, serialization, or augmentation, before being sent to the server. The name of this method is a bit confusing, as it doesn't actually return a JSON string — but I'm afraid that it's the way that the JavaScript API for `JSON.stringify` works. | ||
Calls [serialize](#ampersand-state-serialize) to determine which values to return in the object. Will be called implicitly by JSON.stringify. | ||
Calls [serialize](#ampersand-state-serialize) to determine which values to return in the object. Will be called implicitly by `JSON.stringify`. | ||
@@ -619,0 +627,0 @@ ```javascript |
@@ -989,1 +989,39 @@ /*jshint expr: true*/ | ||
}); | ||
test('#53 - previousAttributes set correctly when it was a default', function (t) { | ||
var MyState = State.extend({ | ||
props: { | ||
test1: ['boolean', true, true], | ||
test2: ['boolean', true, true] | ||
} | ||
}); | ||
var a = new MyState(); | ||
a.on('change:test1', function () { | ||
t.deepEqual(a.previousAttributes(), { | ||
test1: true, | ||
test2: true | ||
}); | ||
t.end(); | ||
}); | ||
a.test1 = false; | ||
}); | ||
test('#74 - ensure default array/object types are mutable', function (t) { | ||
var MyState = State.extend({ | ||
props: { | ||
anArray: ['array', true], | ||
anObject: ['object', true] | ||
} | ||
}); | ||
var s = new MyState(); | ||
s.anArray.push(1); | ||
t.equal(s.anArray.length, 1); | ||
t.equal(s.anArray[0], 1); | ||
s.anObject.foo = 'bar'; | ||
t.equal(s.anObject.foo, 'bar'); | ||
t.end(); | ||
}); |
@@ -1494,2 +1494,34 @@ var tape = require('tape'); | ||
test('#128 don\'t coerce null date as 0', function (t) { | ||
var Day = State.extend({ | ||
props: { | ||
theDate: 'date' | ||
} | ||
}); | ||
var day = new Day({ theDate: null }); | ||
t.notOk(day.theDate, 'date should not be set if null'); | ||
t.equal(day.theDate, null); | ||
day = new Day({ theDate: undefined }); | ||
t.notOk(day.theDate, 'date should not be set if undefined'); | ||
t.equal(day.theDate, undefined); | ||
var Day2 = State.extend({ | ||
props: { | ||
theDate: { | ||
type: 'date', | ||
required: true, | ||
allowNull: false | ||
} | ||
} | ||
}); | ||
t.throws(function () { | ||
new Day2({ theDate: null }); | ||
}, /cannot be null/, 'if allowNull:false, and required:true should still throw'); | ||
t.end(); | ||
}); | ||
test('#68, #110 mixin props should not be deleted', function (t) { | ||
@@ -1496,0 +1528,0 @@ var SelectedMixin = { |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
123857
3041
628
1