Merge pull request #2822 from zpao/rm-immutable

Remove LegacyImmutableObject
This commit is contained in:
Paul O’Shannessy
2015-01-07 13:27:21 -08:00
2 changed files with 0 additions and 480 deletions
-187
View File
@@ -1,187 +0,0 @@
/**
* Copyright 2013-2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule LegacyImmutableObject
* @typechecks
*/
"use strict";
var assign = require('Object.assign');
var invariant = require('invariant');
var isNode = require('isNode');
var mergeHelpers = require('mergeHelpers');
var checkMergeObjectArgs = mergeHelpers.checkMergeObjectArgs;
var isTerminal = mergeHelpers.isTerminal;
/**
* Wrapper around JavaScript objects that provide a guarantee of immutability at
* developer time when strict mode is used. The extra computations required to
* enforce immutability is stripped out in production for performance reasons.
*/
var LegacyImmutableObject;
function assertLegacyImmutableObject(immutableObject) {
invariant(
immutableObject instanceof LegacyImmutableObject,
'LegacyImmutableObject: Attempted to set fields on an object that is not ' +
'an instance of LegacyImmutableObject.'
);
}
if (__DEV__) {
/**
* Constructs an instance of `LegacyImmutableObject`.
*
* @param {?object} initialProperties The initial set of properties.
* @constructor
*/
LegacyImmutableObject = function LegacyImmutableObject(initialProperties) {
assign(this, initialProperties);
deepFreeze(this);
};
/**
* Checks if an object should be deep frozen. Instances of
* `LegacyImmutableObject` are assumed to have already been deep frozen.
*
* @param {*} object The object to check.
* @return {boolean} Whether or not deep freeze is needed.
*/
var shouldRecurseFreeze = function(object) {
return (
typeof object === 'object' &&
!(object instanceof LegacyImmutableObject) &&
object !== null
);
};
/**
* Freezes the supplied object deeply.
*
* @param {*} object The object to freeze.
*/
var deepFreeze = function(object) {
if (isNode(object)) {
return; // Don't try to freeze DOM nodes.
}
Object.freeze(object); // First freeze the object.
for (var prop in object) {
if (object.hasOwnProperty(prop)) {
var field = object[prop];
if (shouldRecurseFreeze(field)) {
deepFreeze(field);
}
}
}
};
/**
* Returns a new LegacyImmutableObject that is identical to the supplied
* object but with the supplied changes, `put`.
*
* @param {LegacyImmutableObject} immutableObject Starting object.
* @param {?object} put Fields to merge into the object.
* @return {LegacyImmutableObject} The result of merging in `put` fields.
*/
LegacyImmutableObject.set = function(immutableObject, put) {
assertLegacyImmutableObject(immutableObject);
var totalNewFields = assign({}, immutableObject, put);
return new LegacyImmutableObject(totalNewFields);
};
} else {
/**
* Constructs an instance of `LegacyImmutableObject`.
*
* @param {?object} initialProperties The initial set of properties.
* @constructor
*/
LegacyImmutableObject = function LegacyImmutableObject(initialProperties) {
assign(this, initialProperties);
};
/**
* Returns a new LegacyImmutableObject that is identical to the supplied
* object but with the supplied changes, `put`.
*
* @param {LegacyImmutableObject} immutableObject Starting object.
* @param {?object} put Fields to merge into the object.
* @return {LegacyImmutableObject} The result of merging in `put` fields.
*/
LegacyImmutableObject.set = function(immutableObject, put) {
assertLegacyImmutableObject(immutableObject);
var newMap = new LegacyImmutableObject(immutableObject);
assign(newMap, put);
return newMap;
};
}
/**
* Sugar for:
* `LegacyImmutableObject.set(LegacyImmutableObject, {fieldName: putField})`.
*
* @param {LegacyImmutableObject} immutableObject Object on which to set field.
* @param {string} fieldName Name of the field to set.
* @param {*} putField Value of the field to set.
* @return {LegacyImmutableObject} [description]
*/
LegacyImmutableObject.setField = function(immutableObject, fieldName, putField) {
var put = {};
put[fieldName] = putField;
return LegacyImmutableObject.set(immutableObject, put);
};
/**
* Returns a new LegacyImmutableObject that is identical to the supplied object
* but with the supplied changes recursively applied.
*
* @param {LegacyImmutableObject} immutableObject Object on which to set fields.
* @param {object} put Fields to merge into the object.
* @return {LegacyImmutableObject} The result of merging in `put` fields.
*/
LegacyImmutableObject.setDeep = function(immutableObject, put) {
assertLegacyImmutableObject(immutableObject);
return _setDeep(immutableObject, put);
};
function _setDeep(object, put) {
checkMergeObjectArgs(object, put);
var totalNewFields = {};
// To maintain the order of the keys, copy the base object's entries first.
var keys = Object.keys(object);
for (var ii = 0; ii < keys.length; ii++) {
var key = keys[ii];
if (!put.hasOwnProperty(key)) {
totalNewFields[key] = object[key];
} else if (isTerminal(object[key]) || isTerminal(put[key])) {
totalNewFields[key] = put[key];
} else {
totalNewFields[key] = _setDeep(object[key], put[key]);
}
}
// Apply any new keys that the base object didn't have.
var newKeys = Object.keys(put);
for (ii = 0; ii < newKeys.length; ii++) {
var newKey = newKeys[ii];
if (object.hasOwnProperty(newKey)) {
continue;
}
totalNewFields[newKey] = put[newKey];
}
return (object instanceof LegacyImmutableObject ||
put instanceof LegacyImmutableObject) ?
new LegacyImmutableObject(totalNewFields) :
totalNewFields;
}
module.exports = LegacyImmutableObject;
@@ -1,293 +0,0 @@
/**
* Copyright 2013-2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
"use strict";
require('mock-modules')
.dontMock('LegacyImmutableObject');
var LegacyImmutableObject;
/**
* To perform performance testing of using `LegacyImmutableObject` vs. not using
* `LegacyImmutableObject`, such testing must be done with __DEV__ set to false.
*/
describe('LegacyImmutableObject', function() {
var message;
beforeEach(function() {
require('mock-modules').dumpCache();
LegacyImmutableObject = require('LegacyImmutableObject');
this.addMatchers({
/**
* Equivalent with respect to serialization. Must stringify because
* constructors are different and other comparison methods will not
* consider them structurally equal. Probably not useful for use outside
* of this test module.
*/
toBeSeriallyEqualTo: function(expected) {
var actual = this.actual;
var notText = this.isNot ? " not" : "";
this.message = function() {
return "Expected " + JSON.stringify(actual) + notText +
" to be serially equal to " + JSON.stringify(expected);
};
return JSON.stringify(actual) === JSON.stringify(expected);
}
});
});
/**
* We are in __DEV__ by default.
*/
var testDev = function(message, testFunc) {
it(message, testFunc);
};
var testProd = function(message, testFunc) {
// Temporarily enter production mode
window.__DEV__ = false;
it(message, testFunc);
window.__DEV__ = true;
};
var testDevAndProd = function(message, testFunc) {
testDev(message + ':DEV', testFunc);
testProd(message + ':PROD', testFunc);
};
testDev('should be running in DEV', function() {
expect(window.__DEV__).toBe(true);
});
testDev('should not require initial map to be an object', function() {
// These won't throw because they're coerced
expect(function() {
new LegacyImmutableObject([1,2,3]);
}).not.toThrow();
expect(function() {
new LegacyImmutableObject('asdf');
}).not.toThrow();
expect(function() {
new LegacyImmutableObject({oldField: 'asdf', fieldTwo: null});
}).not.toThrow();
});
testDev('should not exceed maximum call stack size with nodes', function() {
var node = document.createElement('div');
var object = new LegacyImmutableObject({node: node});
expect(object.node).toBe(node);
});
testDevAndProd('should not throw when not mutating directly', function() {
var io = new LegacyImmutableObject({oldField: 'asdf'});
expect(function() {
LegacyImmutableObject.set(io, {newField: null}); // not a mutation!
}).not.toThrow();
});
testDev('should prevent shallow field addition when strict', function() {
expect(function() {
var io = new LegacyImmutableObject({oldField: 'asdf'});
io.newField = 'this will not work';
}).toThrow();
});
testDev('should prevent shallow field mutation when strict', function() {
expect(function() {
var io = new LegacyImmutableObject({oldField: 'asdf'});
io.oldField = 'this will not work!';
}).toThrow();
});
testDev('should prevent deep field addition when strict', function() {
expect(function() {
var io =
new LegacyImmutableObject(
{shallowField: {deepField: {oldField: null}}}
);
io.shallowField.deepField.oldField = 'this will not work!';
}).toThrow();
});
testDev('should prevent deep field mutation when strict', function() {
expect(function() {
var io =
new LegacyImmutableObject(
{shallowField: {deepField: {oldField: null}}}
);
io.shallowField.deepField.newField = 'this will not work!';
}).toThrow();
});
testDevAndProd(
'should create object with same structure when set with {}',
function() {
var beforeIO =
new LegacyImmutableObject(
{shallowField: {deepField: {oldField: null}}}
);
var afterIO = LegacyImmutableObject.set(beforeIO, {});
expect(afterIO).toBeSeriallyEqualTo(beforeIO);
expect(afterIO).not.toBe(beforeIO);
}
);
testDevAndProd(
'should create distinct object with shallow field insertion',
function() {
var beforeStructure = {
oldShallowField: {
deepField: {
oldField: null
}
}
};
var delta = {
newShallowField: 'newShallowFieldHere'
};
var expectedAfterStructure = {
oldShallowField: {
deepField: {
oldField: null
}
},
newShallowField: 'newShallowFieldHere'
};
var beforeIO = new LegacyImmutableObject(beforeStructure);
var afterIO = LegacyImmutableObject.set(beforeIO, delta);
expect(afterIO).toBeSeriallyEqualTo(expectedAfterStructure);
expect(afterIO).not.toBe(beforeIO);
}
);
testDevAndProd(
'should create distinct object with shallow field mutation',
function() {
var beforeStructure = {
oldShallowField: {
deepField: {
oldField: null
}
}
};
var delta = {
oldShallowField: 'this will clobber the old field'
};
var expectedAfterStructure = {
oldShallowField: 'this will clobber the old field'
};
var beforeIO = new LegacyImmutableObject(beforeStructure);
var afterIO = LegacyImmutableObject.set(beforeIO, delta);
expect(afterIO).toBeSeriallyEqualTo(expectedAfterStructure);
expect(afterIO).not.toBe(beforeIO);
}
);
message = 'should create distinct object with deep field insertion';
testDevAndProd(message, function() {
var beforeStructure = {
oldShallowField: {
deepField: {
oldField: null
}
}
};
var delta = {
oldShallowField: {newDeepField: 'hello'}
};
// LegacyImmutableObject does not yet support deep merging with
// LegacyImmutableObject.set().
var expectedAfterStructure = {
oldShallowField: {newDeepField: 'hello'}
};
var beforeIO = new LegacyImmutableObject(beforeStructure);
var afterIO = LegacyImmutableObject.set(beforeIO, delta);
expect(afterIO).toBeSeriallyEqualTo(expectedAfterStructure);
expect(afterIO).not.toBe(beforeIO);
});
message =
'should tolerate arrays at deeper levels and prevent mutation on them';
testDevAndProd(message, function() {
if (window.callPhantom) {
// PhantomJS has a bug with Object.freeze and Arrays.
// https://github.com/ariya/phantomjs/issues/10817
return;
}
var beforeStructure = {
shallowField: [1,'second field',3]
};
var io = new LegacyImmutableObject(beforeStructure);
expect(function() {
io.newField = 'nope!';
}).toThrow();
expect(function() {
io.shallowField[0] = 'nope!';
}).toThrow();
expect(io.shallowField[1]).toEqual('second field');
});
message = 'should provide a setField interface as sugar for set()';
testDevAndProd(message, function() {
var beforeIO = new LegacyImmutableObject({initialField: null});
var afterIO =
LegacyImmutableObject.setField(beforeIO, 'anotherField', 'anotherValue');
expect(afterIO).toBeSeriallyEqualTo({
initialField: null,
anotherField: 'anotherValue'
});
expect(afterIO).not.toBe(beforeIO);
});
message = 'should recursively create distinct objects when deep copying';
testDevAndProd(message, function() {
var beforeIO = new LegacyImmutableObject({
a: {b: 'b', c: {}, d: 'd', e: new LegacyImmutableObject({f: 'f'}) }
});
var afterIO = LegacyImmutableObject.setDeep(beforeIO, {
a: {b: {}, c: 'C', e: {f: 'F', g: 'G'}, h: 'H'}
});
expect(afterIO).toBeSeriallyEqualTo({
a: {b: {}, c: 'C', d: 'd', e: {f: 'F', g: 'G'}, h: 'H'}
});
expect(afterIO).not.toBe(beforeIO);
expect(afterIO.a).not.toBe(beforeIO.a);
expect(afterIO.a.e).not.toBe(beforeIO.a.e);
});
testDevAndProd('should deep copy member immutability', function() {
var beforeIO = new LegacyImmutableObject({
a: {b: new LegacyImmutableObject({c: 'c'}), e: {f: 'f'}}
});
var afterIO = LegacyImmutableObject.setDeep(beforeIO, {
a: {b: {d: 'D'}, e: new LegacyImmutableObject({g: 'G'})}
});
expect(afterIO).toBeSeriallyEqualTo({
a: {b: {c: 'c', d: 'D'}, e: {f: 'f', g: 'G'}}
});
expect(afterIO instanceof LegacyImmutableObject).toBe(true);
expect(afterIO.a.b instanceof LegacyImmutableObject).toBe(true);
expect(afterIO.a.e instanceof LegacyImmutableObject).toBe(true);
});
});