immutable
Advanced tools
Comparing version 0.8.0 to 0.9.0
{ | ||
"name": "immutable", | ||
"description": "immutable data-structures in javascript.", | ||
"version": "0.8.0", | ||
"description": "effecient immutable data-structures in javascript.", | ||
"version": "0.9.0", | ||
"main": "src/immutable.js", | ||
"scripts": { | ||
"test" : "node test/runner.js test/" | ||
}, | ||
"keywords": [ | ||
"immutability", | ||
"functional programming", | ||
"fp", | ||
"persistent datastructures" | ||
], | ||
"author": "hughfdjackson", | ||
"license": "BSD", | ||
"repository": { | ||
@@ -15,6 +20,8 @@ "type": "git", | ||
"string-hash": "1.1.0", | ||
"persistent-hash-trie": "0.2.x" | ||
"persistent-hash-trie": "0.3.2" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/mocha/bin/mocha" | ||
}, | ||
"devDependencies": { | ||
"benchmark": "1.0.0", | ||
"underscore": "*", | ||
@@ -40,11 +47,4 @@ "mocha": "1.8.1" | ||
], | ||
"harness": "mocha-qunit" | ||
}, | ||
"keywords": [ | ||
"immutability", | ||
"functional programming", | ||
"persistent datastructures" | ||
], | ||
"author": "hughfdjackson", | ||
"license": "BSD" | ||
"harness": "mocha" | ||
} | ||
} |
118
src/array.js
'use strict' | ||
var u = require('./util') | ||
var object = require('./object') | ||
var p = require('persistent-hash-trie') | ||
var util = require('./util') | ||
var secret = {} | ||
// exported constructor | ||
// -- accepts attrs, and auto-assocs them on | ||
// -- as sugar | ||
module.exports = function(attrs){ | ||
return (new array()).assoc(attrs) | ||
} | ||
var array = function(attrs){ | ||
if ( !(this instanceof array) ) return new array(attrs) | ||
var store = p.Trie({}) | ||
this['-data'] = function(s, data){ | ||
if ( s === secret && data ) return store = data | ||
else return store | ||
} | ||
return attrs ? this.assoc(attrs) : this | ||
// helper assoc functions, to help support the variadicness of | ||
// array.prototype.assoc | ||
var assocMultiple = function(arr, attrs){ | ||
for ( var p in attrs ) arr = arr.assoc(p, attrs[p]) | ||
return arr | ||
} | ||
array.prototype = { | ||
var assocOne = function(arr, trie, key, value){ | ||
var keyAsLength = parseInt(key, 10) + 1 | ||
var length = Math.max(arr.length, keyAsLength || 0) | ||
constructor: array, | ||
var newTrie = p.assoc(trie, key.toString(), value) | ||
return new array(newTrie, length) | ||
} | ||
// stealing from object | ||
// internal constructor | ||
var array = function(trie, length){ | ||
trie = trie || p.Trie() | ||
assoc: function(k, v){ | ||
if ( typeof k === 'object' && typeof k !== null ) { | ||
var keys = Object.keys(k) | ||
return keys.reduce(function(object, key){ return object.assoc(key, k[key]) }, this) | ||
} | ||
var t = p.assoc(this['-data'](secret), k, v) | ||
var ret = new array() | ||
ret['-data'](secret, t) | ||
this.length = length || 0 | ||
ret.length = this.length > k ? this.length : parseInt(k, 10) + 1 | ||
Object.freeze(this) | ||
return ret | ||
}, | ||
this.assoc = function(arg1, arg2){ | ||
if ( arguments.length === 1 ) return assocMultiple(this, arg1) | ||
else return assocOne(this, trie, arg1, arg2) | ||
} | ||
dissoc: object.prototype.dissoc, | ||
this.dissoc = function(key){ | ||
var newTrie = p.dissoc(trie, key.toString()) | ||
return new array(newTrie, this.length) | ||
} | ||
get: object.prototype.get, | ||
has: object.prototype.has, | ||
this.get = function(key){ | ||
return p.get(trie, key.toString()) | ||
} | ||
transient: function(){ | ||
return u.extend([], p.transient(this['-data'](secret))) | ||
}, | ||
push: function(){ | ||
var args = u.slice(arguments) | ||
return this.concat(args) | ||
}, | ||
pop: function(){ | ||
return this.slice(0, -1) | ||
}, | ||
unshift: function(){ | ||
return new array(u.slice(arguments)).concat(this.transient()) | ||
}, | ||
shift: function(){ | ||
return this.slice(1, this.length) | ||
}, | ||
concat: function(a){ | ||
if ( a instanceof array ) return this.concat(a.transient()) | ||
else return new array(this.transient().concat(a)) | ||
this.has = function(key){ | ||
return p.has(trie, key.toString()) | ||
} | ||
} | ||
var retPrim = u.pick(Array.prototype, 'toString', 'toLocaleString', 'indexOf', 'lastIndexOf', 'some', 'every') | ||
var retArr = u.pick(Array.prototype, 'join', 'reverse', 'slice', 'splice', 'sort', 'filter', 'forEach', 'map') | ||
var retAny = u.pick(Array.prototype, 'reduce', 'reduceRight') | ||
this.mutable = this.toJSON = function(){ | ||
return util.extend([], p.mutable(trie)) | ||
} | ||
var wrapPrim = function(fn){ | ||
return function(){ | ||
var t = this.transient() | ||
return fn.apply(t, arguments) | ||
} | ||
util.freeze(this) | ||
} | ||
var wrapArr = function(fn){ | ||
return function(){ | ||
var t = this.transient() | ||
return new array(fn.apply(t, arguments)) | ||
} | ||
} | ||
u.extend(array.prototype, u.mapObj(retPrim, wrapPrim)) | ||
u.extend(array.prototype, u.mapObj(retAny, wrapPrim)) | ||
u.extend(array.prototype, u.mapObj(retArr, wrapArr)) | ||
module.exports = array | ||
// prototype to both constructors | ||
// -- so that `immutable.array() instanceof immutable.array` is true, | ||
// -- and extending the prototype works as expected | ||
module.exports.prototype = array.prototype = { | ||
// futher cementing the lie that the prototype 'belongs' to the exported | ||
// constructor | ||
constructor: module.exports | ||
} |
'use strict' | ||
var p = require('persistent-hash-trie') | ||
var util = require('./util') | ||
var secret = {} | ||
// exported constructor | ||
// -- accepts attrs, and auto-assocs them on | ||
// -- as sugar | ||
module.exports = function(attrs){ | ||
return (new object()).assoc(attrs) | ||
} | ||
var object = module.exports = function(attrs){ | ||
if ( !(this instanceof object) ) return new object(attrs) | ||
var store = p.Trie() | ||
// helper assoc functions, to help support the variadicness of | ||
// object.prototype.assoc | ||
var assocMultiple = function(obj, attrs){ | ||
for ( var p in attrs ) obj = obj.assoc(p, attrs[p]) | ||
return obj | ||
} | ||
this['-data'] = function(s, data){ | ||
if ( s === secret && data ) return store = data | ||
else return store | ||
} | ||
Object.freeze(this) | ||
return attrs ? this.assoc(attrs) : this | ||
var assocOne = function(trie, key, value){ | ||
return new object(p.assoc(trie, key.toString(), value)) | ||
} | ||
object.prototype = { | ||
constructor: object, | ||
// internal constructor | ||
var object = function(trie){ | ||
trie = trie || p.Trie() | ||
assoc: function(k, v){ | ||
if ( typeof k === 'object' && typeof k !== null ) { | ||
var keys = Object.keys(k) | ||
return keys.reduce(function(object, key){ return object.assoc(key, k[key]) }, this) | ||
} | ||
var t = p.assoc(this['-data'](secret), k, v) | ||
var ret = new object() | ||
ret['-data'](secret, t) | ||
return ret | ||
}, | ||
// assoc returns a new object with values associated across. | ||
// supports either an object, or a key and a value | ||
this.assoc = function(arg1, arg2){ | ||
if ( arguments.length === 1 ) return assocMultiple(this, arg1) | ||
else return assocOne(trie, arg1, arg2) | ||
} | ||
dissoc: function(k){ | ||
var t = p.dissoc(this['-data'](secret), k) | ||
var ret = new object() | ||
ret['-data'](secret, t) | ||
return ret | ||
}, | ||
this.dissoc = function(key){ | ||
return new object(p.dissoc(trie, key.toString())) | ||
} | ||
get: function(k){ | ||
k = k.toString() | ||
return p.get(this['-data'](secret), k) | ||
}, | ||
this.get = function(key){ | ||
return p.get(trie, key.toString()) | ||
} | ||
has: function(k){ | ||
return p.has(this['-data'](secret), k) | ||
}, | ||
this.has = function(key){ | ||
return p.has(trie, key.toString()) | ||
} | ||
transient: function(){ | ||
return p.transient(this['-data'](secret)) | ||
this.mutable = this.toJSON = function(){ | ||
return p.mutable(trie) | ||
} | ||
util.freeze(this) | ||
} | ||
// prototype to both constructors | ||
// -- so that `immutable.object() instanceof immutable.object` is true, | ||
// -- and extending the prototype works as expected | ||
module.exports.prototype = object.prototype = { | ||
// futher cementing the lie that the prototype 'belongs' to the exported | ||
// constructor | ||
constructor: module.exports | ||
} |
@@ -15,8 +15,13 @@ 'use strict' | ||
var pick = function(o){ | ||
var names = slice(arguments, 1), | ||
r = {} | ||
names.forEach(function(p){ r[p] = o[p] }) | ||
var names = slice(arguments, 1) | ||
var r = {} | ||
for ( var i = 0; i < names.length; i += 1 ) | ||
r[names[i]] = o[names[i]] | ||
return r | ||
} | ||
var freeze = Object.freeze || function(o){ return o } | ||
module.exports = { | ||
@@ -27,3 +32,4 @@ extend : extend, | ||
mapObj : mapObj, | ||
pick : pick | ||
pick : pick, | ||
freeze : freeze | ||
} |
@@ -1,151 +0,73 @@ | ||
var a = require('assert'), | ||
p = require('..') | ||
var a = require('assert') | ||
var im = require('..') | ||
suite('p.array') | ||
describe('im.array', function(){ | ||
test('has right constructor', function(){ | ||
a.equal(p.array.prototype.constructor, p.array) | ||
}) | ||
it('freezes arrays on creation if available', function(){ | ||
if ( !Object.freeze ) return | ||
test('-data is a function', function(){ | ||
var arr = p.array([]) | ||
a.equal(typeof arr['-data'], 'function') | ||
}) | ||
var arr = im.array() | ||
test('takes p.object\'s methods', function(){ | ||
var arr = p.array(), | ||
object = p.object() | ||
arr.x = 3 | ||
a.equal(a.x, undefined) | ||
}) | ||
a.equal(arr.get, object.get) | ||
a.equal(arr.has, object.has) | ||
a.equal(arr.dissoc, object.dissoc) | ||
}) | ||
describe('.assoc', function(){ | ||
test('array.assoc returns array', function(){ | ||
var arr = p.array().assoc(1, '2').assoc({ x: 3 }).assoc([1, 2, 3]) | ||
it('should allow a new version to be made with added properties', function(){ | ||
a.ok(arr instanceof p.array) | ||
}) | ||
var arr1 = im.array() | ||
.assoc(1, 'first') | ||
.assoc({ x: 'x val' }) | ||
test('array.transient returns array with *all* props copied', function(){ | ||
var arr = p.array({ x: 3, 0: 1, 2: 3 }).transient() | ||
var arr2 = arr1.assoc(['a', 'b', 'c']) | ||
a.equal(arr[0], 1) | ||
a.equal(arr[2], 3) | ||
a.equal(arr.length, 3) | ||
a.equal(arr1.get(1), 'first') | ||
a.equal(arr1.get('x'), 'x val') | ||
a.equal(arr1.length, 2) | ||
a.equal(arr.x, 3) | ||
a.ok(Array.isArray(arr)) | ||
}) | ||
a.equal(arr2.get(1), 'b') | ||
}) | ||
test('push', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
it('should return an im.array', function(){ | ||
a.ok(im.array() instanceof im.array) | ||
}) | ||
}) | ||
arr.push(4, 5, 6) | ||
a.deepEqual(arr.transient(), [1, 2, 3]) | ||
describe('length', function(){ | ||
it('should get updated to be the largest int + 1', function(){ | ||
var arr1 = im.array([1, 2, 3]) | ||
a.equal(arr1.length, 3) | ||
arr = arr.push(4, 5, 6) | ||
a.deepEqual(arr.transient(), [1, 2, 3, 4, 5, 6]) | ||
}) | ||
var arr2 = arr1.assoc(3, 1) | ||
a.equal(arr2.length, 4) | ||
test('pop', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
var arr3 = arr1.assoc(4, 1) | ||
a.equal(arr3.length, 5) | ||
arr.pop() | ||
a.deepEqual(arr.transient(), [1, 2, 3]) | ||
var arr4 = arr1.assoc(100, 3) | ||
a.equal(arr4.length, 101) | ||
}) | ||
arr = arr.pop() | ||
a.deepEqual(arr.transient(), [1, 2]) | ||
}) | ||
arr = p.array([]) | ||
arr = arr.pop() | ||
describe('.mutable', function(){ | ||
it('should return an array with all properties copied', function(){ | ||
var arr = im.array({ x: 3, 0: 1, 2: 3 }).mutable() | ||
a.deepEqual(arr.transient(), []) | ||
}) | ||
a.equal(arr[0], 1) | ||
a.equal(arr[2], 3) | ||
a.equal(arr.length, 3) | ||
a.equal(arr.x, 3) | ||
a.ok(arr instanceof Array) | ||
}) | ||
}) | ||
test('unshift', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
arr.unshift(4, 5, 6) | ||
a.deepEqual(arr.transient(), [1, 2, 3]) | ||
arr = arr.unshift(4, 5, 6) | ||
a.deepEqual(arr.transient(), [4, 5, 6, 1, 2, 3]) | ||
describe('.toJSON', function(){ | ||
it('should be an alias for .mutable', function(){ | ||
var arr = im.array() | ||
a.equal(arr.mutable, arr.toJSON) | ||
}) | ||
}) | ||
}) | ||
test('shift', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
arr.shift() | ||
a.deepEqual(arr.transient(), [1, 2, 3]) | ||
arr = arr.shift() | ||
a.deepEqual(arr.transient(), [2, 3]) | ||
arr = p.array([]) | ||
arr = arr.shift() | ||
a.deepEqual(arr.transient(), []) | ||
}) | ||
test('concat', function(){ | ||
var arr = p.array([1, 2, 3]), | ||
res = arr.concat([4, 5, 6]) | ||
a.deepEqual(res.transient(), [1, 2, 3, 4, 5, 6]) | ||
res = arr.concat(arr) | ||
a.deepEqual(res.transient(), [1, 2, 3, 1, 2, 3]) | ||
}) | ||
test('length', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
a.equal(arr.length, 3) | ||
arr = arr.push(1) | ||
a.equal(arr.length, 4) | ||
arr = arr.assoc(100, 3) | ||
a.equal(arr.length, 101) | ||
}) | ||
test('wraps all native methods', function(){ | ||
var assertNotSameAsPrototype = function(name){ a.notEqual(p.array.prototype[name], Array.prototype[name]) } | ||
;["toString", "toLocaleString", "join", "pop", "push", "concat", "reverse", "slice", | ||
"splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", | ||
"reduce", "reduceRight"].forEach(assertNotSameAsPrototype) | ||
}) | ||
test('.filter - an object returning wrapped method - rewraps in p.array', function(){ | ||
var arr = p.array([1, 2, 3]), | ||
odd = function(n){ return n % 2 !== 0 }, | ||
res = arr.filter(odd) | ||
a.ok(res instanceof p.array) | ||
a.ok(arr.get(1), 2) | ||
a.ok(res.get(1), undefined) | ||
}) | ||
test('.every - a non-object returning wrapped method', function(){ | ||
var arr = p.array([1, 2, 3]), | ||
odd = function(n){ return n % 2 !== 0 } | ||
a.equal(arr.every(odd), false) | ||
a.equal(arr.some(odd), true) | ||
}) | ||
test('.reduce and .reduceRight return the same type', function(){ | ||
var arr = p.array([1, 2, 3]) | ||
var res = arr.reduce(function(arr, i) { arr.push(i + 1); return arr }, []) | ||
a.ok(!(res instanceof p.array)) | ||
res = arr.reduceRight(function(arr, i) { arr.push(i + 1); return arr }, []) | ||
a.ok(!(res instanceof p.array)) | ||
}) |
var a = require('assert') | ||
var p = require('..') | ||
var im = require('..') | ||
suite('p.object') | ||
describe('im.object', function(){ | ||
it('has the right constructor', function(){ | ||
a.equal(im.object.prototype.constructor, im.object) | ||
}) | ||
test('has right constructor', function(){ | ||
a.equal(p.object.prototype.constructor, p.object) | ||
}) | ||
it('freezes object on creation if Object.freeze is available', function(){ | ||
if ( ! Object.freeze ) return | ||
suite('p.objects') | ||
var o = im.object({ }) | ||
test('are frozen', function(){ | ||
var d = p.object({ }) | ||
o.x = 3 | ||
a.equal(o.x, undefined) | ||
}) | ||
d.x = 3 | ||
a.equal(d.x, undefined) | ||
}) | ||
describe('creation', function(){ | ||
it('should be a newless constructor', function(){ | ||
a.ok(im.object() instanceof im.object) | ||
}) | ||
test('-data is a function', function(){ | ||
var d = p.object({ }) | ||
a.equal(typeof d['-data'], 'function') | ||
}) | ||
it('creates an empty object if no props are passed', function(){ | ||
var o = im.object() | ||
a.deepEqual(o.mutable(), {}) | ||
}) | ||
it('creates an object with props passed in', function(){ | ||
var props = { x: 'y', z: 'wibble' } | ||
var o = im.object(props) | ||
a.deepEqual(o.mutable(), props) | ||
}) | ||
}) | ||
test('object()', function(){ | ||
var o = p.object() | ||
a.ok(o) | ||
}) | ||
describe('.assoc', function(){ | ||
it('returns a new immutable object with props updated', function(){ | ||
var obj1 = im.object() | ||
var obj2 = obj1.assoc('x', 3) | ||
var obj3 = obj1.assoc({ y: 'x' }) | ||
test('object(Object)', function(){ | ||
var opts = { x: 1, y: 2 }, | ||
o = p.object(opts) | ||
a.deepEqual(obj1.mutable(), {}) | ||
a.deepEqual(obj2.mutable(), { x: 3 }) | ||
a.deepEqual(obj3.mutable(), { y: 'x' }) | ||
}) | ||
}) | ||
a.ok(o.has('x')) | ||
delete opts.x | ||
a.ok(o.has('x')) | ||
}) | ||
describe('.dissoc', function(){ | ||
it('returns a new immutable object with props removed', function(){ | ||
test('object.assoc(k, v)', function(){ | ||
var o = p.object(), | ||
o2 = o.assoc('x', 3) | ||
var obj1 = im.object({ x: 1, y: 1 }) | ||
var obj2 = obj1.dissoc('x') | ||
a.notEqual(o, o2) | ||
}) | ||
a.deepEqual(obj1.mutable(), { x: 1, y: 1 }) | ||
a.deepEqual(obj2.mutable(), { y: 1 }) | ||
}) | ||
}) | ||
test('object.assoc', function(){ | ||
var o1 = p.object({ 'x': 3 }), | ||
o2 = o1.assoc('y', 3).assoc({ 'z': 3 }) | ||
describe('.get', function(){ | ||
it('should return a value of a stored property, or else undefined', function(){ | ||
var o = im.object({ x: 3 }) | ||
a.equal(o.get('x'), 3) | ||
a.equal(o.get('y'), undefined) | ||
}) | ||
}) | ||
a.ok(o1.has('x')) | ||
a.ok(!o1.has('y')) | ||
a.ok(!o1.has('z')) | ||
describe('.has', function(){ | ||
it('should return true or false, indicating whether a property exists on the prop', function(){ | ||
var o = im.object({ x: 3 }) | ||
a.equal(o.has('x'), true) | ||
a.equal(o.has('y'), false) | ||
}) | ||
}) | ||
a.ok(o2.has('x')) | ||
a.ok(o2.has('y')) | ||
a.ok(o2.has('z')) | ||
}) | ||
describe('.mutable', function(){ | ||
it('should return a mutable version of the immutable object', function(){ | ||
var obj1 = im.object({ foo: 'bar' }) | ||
var obj2 = obj1.mutable() | ||
test('object.get', function(){ | ||
var o = p.object().assoc('x', 3) | ||
obj2.bar = 'baz' | ||
a.equal(o.get('x'), 3) | ||
a.equal(o.get('y'), undefined) | ||
}) | ||
a.deepEqual(obj1.mutable(), { foo: 'bar' }) | ||
a.deepEqual(obj2, { bar: 'baz', foo: 'bar' }) | ||
}) | ||
}) | ||
test('object.has', function(){ | ||
var o = p.object().assoc('x', 3) | ||
a.ok(o.has('x')) | ||
a.ok(!o.has('y')) | ||
describe('.toJSON', function(){ | ||
it('should be an alias for mutable', function(){ | ||
var obj = im.object() | ||
a.equal(obj.mutable, obj.toJSON) | ||
}) | ||
}) | ||
}) | ||
test('object.dissoc', function(){ | ||
var o1 = p.object().assoc('x', 3), | ||
o2 = o1.dissoc('x') | ||
a.ok(o1.has('x')) | ||
a.ok(!o2.has('x')) | ||
}) | ||
test('object.delete NOT alias for object.dissoc', function(){ | ||
var o = p.object() | ||
a.equal(o.delete, undefined) | ||
}) | ||
test('p.object is a new-less constructor', function(){ | ||
a.ok(p.object() instanceof p.object) | ||
}) | ||
test('object.transient returns a new mutable object with the same attrs', function(){ | ||
var o = p.object({ foo: 'bar' }), | ||
t = o.transient() | ||
a.ok('foo' in t) | ||
delete t.foo | ||
a.ok(!('foo' in t)) | ||
a.ok(o.has('foo')) | ||
}) |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
2
171
0
13523
9
269
+ Addedpersistent-hash-trie@0.3.2(transitive)
- Removedpersistent-hash-trie@0.2.4(transitive)
Updatedpersistent-hash-trie@0.3.2