dirtyable.js | |
|---|---|
| dirtyable may be freely distributed under the MIT license. | |
| for | var clone = function(obj) {
if (obj !== Object(obj))
return obj;
if (Array.isArray(obj))
return obj.slice();
var out = {},
prop;
for (prop in obj)
out[prop] = obj[prop];
return out;
};
var extend = function(object, keys) { |
Initial SetupInitial type checking. Provide decent error messages here instead of confisuing ones later down the line. | if (typeof arguments[0] !== 'object')
throw new Error('First argument to dirtyable.extend needs to be an object.');
if (!Array.isArray(arguments[1]))
throw new Error('Second argument to dirtyable.extend needs to be an array.'); |
|
| var data = {}, |
| When properties are changed, their original values are stored here
in the format of | changed_properties = {}; |
| Allow use of | Object.defineProperty(changed_properties, 'clear', {
enumerable: false,
value: function() {
for (var i in changed_properties)
delete changed_properties[i];
}
}); |
Object-level property getters | |
| Handler for This returns true if any tracked values on the object have been changed. | var object_isChanged = function() {
return Object.keys(changed_properties).length > 0;
}; |
| Handler for This returns an array of all changed properties. For example: | var object_changed = function() {
return Object.keys(changed_properties);
}; |
| Handler for This returns an object that shows the changes for all properties. For example: | var object_changes = function() {
var out = {};
for (var i in changed_properties)
out[i] = [changed_properties[i], data[i]];
return out;
} |
Property-level property getters (and setter) | |
| Handler for | var property_get = function(property) {
return data[property];
}; |
| Handler for | var property_set = function(property, value) {
if (data[property] == value)
return; |
| Setting a value back to its original value "cleans" it. | if (changed_properties[property] == value) {
delete changed_properties[property];
} else {
changed_properties[property] = data[property];
}
data[property] = value;
}; |
| Handler for `object.property_isChanged like | var property_isChanged = function(property) {
return typeof changed_properties[property] !== 'undefined';
}; |
| Handler for Returns the value of the property before it was changed. | var property_was = function(property) {
return property_isChanged(property)
? changed_properties[property]
: void(0); // TODO: What does rails do here?
} |
| Handler for Like | var property_change = function(property) {
return property_isChanged(property)
? [changed_properties[property], data[property]]
: void(0); // TODO: What does rails do here?
} |
| Handler for This is how you mark something like an array as changed. For example:
| var property_will_change = function(property) {
if (property_isChanged(property))
return;
changed_properties[property] = clone(data[[property]]);
}; |
| Handler for | var reset_property = function(property) {
if (!property_isChanged(property))
return;
property_set(property, changed_properties[property]);
}; |
Object modification starts here | |
Define object-level properties. | Object.defineProperty(object, 'isChanged', {
enumerable: false,
get: object_isChanged
});
Object.defineProperty(object, 'changed', {
enumerable: false,
get: object_changed
})
Object.defineProperty(object, 'changes', {
enumerable: false,
get: object_changes
});
Object.defineProperty(object, 'changedProperties', {
enumerable: false,
value: changed_properties
}) |
Define property-level properties.This is the magic part. It wraps all properties on the object with getters and setters that keep track of changes. | keys.forEach(function(property) { |
| Replace the property with getters and setters. | data[property] = object[property]; |
| Override property with our magic getters and setters. | Object.defineProperty(object, property, {
enumerable: true,
get: property_get.bind(null, property),
set: property_set.bind(null, property)
}); |
Define the prefixed and suffixed properties. | |
| Define | Object.defineProperty(object, property + '_isChanged', {
enumerable: false,
get: property_isChanged.bind(null, property)
}); |
| Define | Object.defineProperty(object, property + '_was', {
enumerable: false,
get: property_was.bind(null, property)
}); |
| Define | Object.defineProperty(object, property + '_change', {
enumerable: false,
get: property_change.bind(null, property)
}); |
| Define | Object.defineProperty(object, property + '_will_change', {
enumerable: false,
value: property_will_change.bind(null, property)
}); |
| Define | Object.defineProperty(object, 'reset_' + property, {
enumerable: false,
value: reset_property.bind(null, property)
});
});
} |
| Expose the object binder. | exports.extend = extend;
|