Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ampersand-state

Package Overview
Dependencies
Maintainers
7
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ampersand-state - npm Package Compare versions

Comparing version 4.8.2 to 4.9.0

128

ampersand-state.js

@@ -23,2 +23,3 @@ 'use strict';

var changeRE = /^change:/;
var noop = function () {};

@@ -120,4 +121,4 @@ function Base(attrs, options) {

var extraProperties = this.extraProperties;
var changing, changes, newType, newVal, def, cast, err, attr,
attrs, dataType, silent, unset, currentVal, initial, hasChanged, isEqual;
var wasChanging, changeEvents, newType, newVal, def, cast, err, attr,
attrs, dataType, silent, unset, currentVal, initial, hasChanged, isEqual, onChange;

@@ -142,8 +143,11 @@ // Handle both `"key", value` and `{key: value}` -style arguments.

changes = [];
changing = this._changing;
// Initialize change tracking.
wasChanging = this._changing;
this._changing = true;
changeEvents = [];
// if not already changing, store previous
if (!changing) {
if (initial) {
this._previousAttributes = {};
} else if (!wasChanging) {
this._previousAttributes = this.attributes;

@@ -154,3 +158,4 @@ this._changed = {};

// For each `set` attribute...
for (attr in attrs) {
for (var i = 0, keys = Object.keys(attrs), len = keys.length; i < len; i++) {
attr = keys[i];
newVal = attrs[attr];

@@ -178,2 +183,3 @@ newType = typeof newVal;

isEqual = this._getCompareForType(def.type);
onChange = this._getOnChangeForType(def.type);
dataType = this._dataTypes[def.type];

@@ -219,44 +225,54 @@

hasChanged = !isEqual(currentVal, newVal, attr);
// We know this has 'changed' if it's the initial set, so skip a potentially expensive isEqual check.
hasChanged = initial || !isEqual(currentVal, newVal, attr);
// enforce `setOnce` for properties if set
if (def.setOnce && currentVal !== undefined && hasChanged && !initial) {
if (def.setOnce && currentVal !== undefined && hasChanged) {
throw new TypeError('Property \'' + attr + '\' can only be set once.');
}
// keep track of changed attributes
// and push to changes array
// set/unset attributes.
// If this is not the initial set, keep track of changed attributes
// and push to changeEvents array so we can fire events.
if (hasChanged) {
changes.push({prev: currentVal, val: newVal, key: attr});
self._changed[attr] = newVal;
// This fires no matter what, even on initial set.
onChange(newVal, currentVal, attr);
// If this is a change (not an initial set), mark the change.
// Note it's impossible to unset on the initial set (it will already be unset),
// so we only include that logic here.
if (!initial) {
this._changed[attr] = newVal;
this._previousAttributes[attr] = currentVal;
if (unset) {
// FIXME delete is very slow. Can we get away with setting to undefined?
delete this._values[attr];
}
if (!silent) {
changeEvents.push({prev: currentVal, val: newVal, key: attr});
}
}
if (!unset) {
this._values[attr] = newVal;
}
} else {
delete self._changed[attr];
// Not changed
// FIXME delete is very slow. Can we get away with setting to undefined?
delete this._changed[attr];
}
}
// actually update our values
changes.forEach(function (change) {
self._previousAttributes[change.key] = change.prev;
if (unset) {
delete self._values[change.key];
} else {
self._values[change.key] = change.val;
}
// Fire events. This array is not populated if we are told to be silent.
if (changeEvents.length) this._pending = true;
changeEvents.forEach(function (change) {
self.trigger('change:' + change.key, self, change.val, options);
});
if (!silent && changes.length) self._pending = true;
if (!silent) {
changes.forEach(function (change) {
self.trigger('change:' + change.key, self, change.val, options);
});
}
// You might be wondering why there's a `while` loop here. Changes can
// be recursively nested within `"change"` events.
if (changing) return this;
if (!silent) {
while (this._pending) {
this._pending = false;
this.trigger('change', this, options);
}
if (wasChanging) return this;
while (this._pending) {
this._pending = false;
this.trigger('change', this, options);
}

@@ -374,2 +390,8 @@ this._pending = false;

_getOnChangeForType : function(type){
var dataType = this._dataTypes[type];
if (dataType && dataType.onChange) return bind(dataType.onChange, this);
return noop;
},
// Run validation against the next complete set of model attributes,

@@ -404,4 +426,4 @@ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.

var res = {};
var val, item, def;
for (item in this._definition) {
var val, def;
for (var item in this._definition) {
def = this._definition[item];

@@ -416,3 +438,3 @@ if ((options.session && def.session) || (options.props && !def.session)) {

if (options.derived) {
for (item in this._derived) res[item] = this[item];
for (var derivedItem in this._derived) res[derivedItem] = this[derivedItem];
}

@@ -606,5 +628,11 @@ return res;

}
value = result(def, 'default');
this._values[name] = value;
return value;
var defaultValue = result(def, 'default');
this._values[name] = defaultValue;
// If we've set a defaultValue, fire a change handler effectively marking
// its change from undefined to the default value.
if (typeof defaultValue !== 'undefined') {
var onChange = this._getOnChangeForType(def.type);
onChange(defaultValue, value, name);
}
return defaultValue;
}

@@ -729,18 +757,16 @@ });

},
compare: function (currentVal, newVal, attributeName) {
var isSame = currentVal === newVal;
compare: function (currentVal, newVal) {
return currentVal === newVal;
},
onChange : function(newVal, previousVal, attributeName){
// if this has changed we want to also handle
// event propagation
if (!isSame) {
if (currentVal) {
this.stopListening(currentVal);
}
if (previousVal) {
this.stopListening(previousVal);
}
if (newVal != null) {
this.listenTo(newVal, 'all', this._getEventBubblingHandler(attributeName));
}
if (newVal != null) {
this.listenTo(newVal, 'all', this._getEventBubblingHandler(attributeName));
}
return isSame;
}

@@ -747,0 +773,0 @@ }

{
"name": "ampersand-state",
"description": "An observable, extensible state object with derived watchable properties.",
"version": "4.8.2",
"version": "4.9.0",
"author": "Henrik Joreteg <henrik@andyet.net>",

@@ -42,12 +42,9 @@ "files": [

"ampersand-registry": "0.x.x",
"browserify": "^11.0.1",
"coveralls": "^2.11.4",
"istanbul": "^0.4.0",
"jshint": "^2.5.3",
"phantomjs": "^1.9.7-15",
"phantomjs": "^1.9.19",
"precommit-hook": "^3.0.0",
"run-browser": "^2.0.2",
"tap-spec": "^4.0.2",
"tape": "^4.0.3",
"tape-run": "^1.1.0"
"zuul": "^3.9.0"
},

@@ -67,8 +64,9 @@ "homepage": "https://github.com/ampersandjs/ampersand-state",

"scripts": {
"test": "browserify test/index.js | tape-run | tap-spec",
"start": "zuul --local -- test/index.js",
"test": "zuul --phantom -- test/index.js",
"test-ci": "zuul -- test/index.js",
"coverage": "rm -rf coverage && istanbul cover -- tape test/index.js --verbose",
"validate": "npm ls",
"start": "run-browser test/index.js",
"lint": "jshint ampersand-state.js ./test/*",
"benchmark": "node --allow-natives-syntax benchmark/massCreate.js",
"benchmark": "for f in benchmark/*.js; do node --allow-natives-syntax --trace-deopt $f; done",
"preversion": "git checkout master && git pull && npm ls",

@@ -80,18 +78,2 @@ "publish-patch": "npm run preversion && npm version patch && git push origin master --tags && npm publish",

},
"testling": {
"files": "test/*.js",
"browsers": [
"ie/9..latest",
"firefox/17..latest",
"firefox/nightly",
"chrome/22..latest",
"chrome/canary",
"opera/12..latest",
"opera/next",
"safari/5.1..latest",
"ipad/6.0..latest",
"iphone/6.0..latest",
"android-browser/4.2..latest"
]
},
"pre-commit": [

@@ -98,0 +80,0 @@ "lint",

@@ -226,2 +226,3 @@ # ampersand-state

* `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}`.
* `onChange : function (value, previousValue, attributeName){};`: Called after the value changes. Useful for automatically setting up or tearing down listeners on properties.
* `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.

@@ -313,2 +314,3 @@ * `default : function(){} returns val;`: Returns the default value for this type.

* If the property doesn't have a `default`, and you don't set the value initially, it can be set later, but only once.
* If `test` function is passed, then a negative validation test will be executed every time this property is about to be set. If the validation passes, the function must return `false` to tell **State** to go ahead and set the value. Otherwise, it should return a `string` with the error message describing the validation failure. In this case **State** will throw a `TypeError` with `"Property '<property>' failed validation with error: <errorMessage>"`.

@@ -328,3 +330,12 @@ Trying to set a property to an invalid type will throw an error.

values: ['regular-hero', 'super-hero', 'mega-hero']
}
},
numberOfChildren: {
type: 'number',
test: function(value){
if (value < 0) {
return "Must be a positive number";
}
return false;
}
},
}

@@ -378,9 +389,19 @@ });

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:
Derived properties (also known as computed properties) are properties of the state object that depend on other properties (from `props`, `session`, or even `derived` or the same from state props or children) to determine their value. Best demonstrated with an example:
```javascript
var Address = AmpersandState.extend({
props: {
'street': 'string',
'city': 'string',
'region': 'string',
'postcode': 'string'
}
});
var Person = AmpersandState.extend({
props: {
firstName: 'string',
lastName: 'string'
lastName: 'string',
address: 'state'
},

@@ -393,2 +414,15 @@ derived: {

}
},
mailingAddress: {
deps: ['address.street', 'address.city', 'address.region', 'address.postcode'],
fn: function () {
var self = this;
return ['street','city','region','postcode'].map(function (prop) {
var val = self.address[prop];
if (!val) return val;
return (prop === 'street' || prop === 'city') ? val + ',' : val;
}).filter(function (val) {
return !!val;
}).join(' ');
}
}

@@ -398,9 +432,23 @@ }

var person = new Person({ firstName: 'Phil', lastName: 'Roberts' });
var person = new Person({
firstName: 'Phil',
lastName: 'Roberts',
address: new Address({
street: '123 Main St',
city: 'Anyplace',
region: 'BC',
postcode: 'V6A 2S5'
})
});
console.log(person.fullName) //=> "Phil Roberts"
console.log(person.mailingAddress) //=> "123 Main St, Anyplace, BC V6A 2S5"
person.firstName = 'Bob';
person.address.street = '321 St. Charles Pl'
console.log(person.fullName) //=> "Bob Roberts"
console.log(person.mailingAddress) //=> "321 St. Charles Pl, Anyplace, BC V6A 2S5"
```
See working example at [RequireBin](http://requirebin.com/?gist=c496f0d33f32527fe1ca)
Each derived property is defined as an object with the following properties:

@@ -407,0 +455,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc