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

corridor

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

corridor - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

test/test-arraylike.js

2

package.json
{
"name": "corridor",
"version": "0.3.1",
"version": "0.4.0",
"description": "JSON/HTML data corridor for data-binding",

@@ -5,0 +5,0 @@ "repository": {

@@ -337,2 +337,49 @@ # corridor

### merging arrays
Merging arrays can be tricky, but in most cases corridor will make a good choice.
In the last section, we looked at an example where the `authors` array contains normal string values.
But let's look at what happens when the values are more complex.
Consider this HTML:
```html
<table data-name="company.employees[]">
<tr>
<td><input type="text" name="name" value="Bob" /></td>
<td><input type="text" name="email" value="bob@company.com" /></td>
</tr>
<tr>
<td><input type="text" name="name" value="Alice" /></td>
<td><input type="text" name="email" value="alice@company.com" /></td>
</tr>
</table>
```
For this, `corridor()` produces the following:
```js
{
"company": {
"employees": [{
"name": "Bob",
"email": "bob@company.com"
},{
"name": "Alice",
"email": "alice@company.com"
}]
}
}
```
The reason this works is that corridor checks each field under an arry to see if it can be safely merged into the last one.
So when it finds Bob's `email`, it knows that it can safely add this key to the preceding Bob object without destroying data.
But when it gets to Alice's `name`, it sees that it couldn't safely add the value.
If it set the last object's `name` to Alice, then the `name` of Bob would be lost.
So it creates a new element and sets its `name` instead.
For more information on array merging, and how you can control it, see the API documentation.
### toggling sections

@@ -346,3 +393,3 @@

```html
<fieldset data-role="role">
<fieldset data-role="toggleable">
<p>

@@ -436,4 +483,4 @@ <label>

corridor(root, data);
corridor(null, data, options);
corridor(root, data, options);
corridor(null, data, opts);
corridor(root, data, opts);
```

@@ -516,2 +563,35 @@

##### merge options
The `merge` option indicates which merging strategy corridor should use when merging two arrays.
Choices are:
* _auto_ - intelligently choose whether to concatenate the arrays, or deep merge them (default)
* _concat_ - concatenate the arrays
* _extend_ - deep merge each pair of items
When in `auto` mode, the algorithm for choosing whether to concatenate or merge two arrays should work as follows:
* if the original array is empty, choose `concat`, otherwise,
* if the length of the other array is greater than one, choose `concat` (this is a strange case), otherwise,
* determine if the last element of the original array can be safely merged with the first element of the second array, if so, recursively merge them, otherwise,
* choose `concat`.
The algorithm for deciding whether an object can be safely merged into a base object is as follows:
* if either argument is a primitive (not an object or array), return false, otherwise,
* if either argument is an array, return true (arrays can always be safely merged), otherwise,
* recursively check the keys of the other object, if any can't be safely merged, return false,
* return true.
Both _auto_ and _concat_ are safe operations.
In neither case is data lost.
However, _extend_ is potentially (likely) unsafe—with this strategy, data is easily clobbered.
In all cases, if either the original or other object is not an array, there is no ambiguity to resolve.
When at least one argument is not array-like, the merge will produce an object such that no information is lost (other than what is specifically overwritten by colliding keys).
For example, if the original object is an array (`["foo"]`) and the other object is a non-array-like object (`{"bar":"baz"}`). then the outcome of the merge will be an object that keeps all data in tact (`{"0":"foo","bar":"baz"}`).
##### toggle options

@@ -518,0 +598,0 @@

@@ -148,4 +148,4 @@ /**

// build out full contribution
contrib = buildup("\ufff0", elem, root);
field = contrib.split("\ufff0").join('$$$');
contrib = buildup("\ufffc", elem, root);
field = contrib.split("\ufffc").join('$$$');

@@ -161,3 +161,3 @@ // short-circuit if this field should be omitted

// inject value into contribution
value = contrib.replace("\ufff0", value);
value = contrib.replace("\ufffc", value);

@@ -210,4 +210,4 @@ // merge contribution into the result data

// build up the target contribution
// starting with "\ufff0" value tag
target = JSON.parse(buildup(JSON.stringify("\ufff0"), elem, root));
// starting with the unicode object replacement character
target = JSON.parse(buildup(JSON.stringify("\ufffc"), elem, root));

@@ -218,3 +218,3 @@ // insert into workspace

// find path to target in workspace
path = locate(workspace, "\ufff0");
path = locate(workspace, "\ufffc");

@@ -298,4 +298,13 @@ // set actual val into workspace to prevent false hits for future fields

*/
enabledOnly: true
enabledOnly: true,
/**
* Strategy to employ when merging two objects.
* Recognized choices are:
* - auto - intelligently merge the objects (default)
* - concat - always concatenate arrays
* - extend - iterate through items and merge them
*/
merge: 'auto'
},

@@ -406,3 +415,3 @@

field = "\ufff0", // start out with the target mark
field = "\ufffc", // object replacement char to start

@@ -417,8 +426,8 @@ parts = name

p = p.replace(/^\s+|\s+$/g, ''); // trim each part
field = field.replace("\ufff0", // add part to field specification
p === '[]' ? "[\ufff0]" : "{" + JSON.stringify(p || 'undefined') + ":\ufff0}"
field = field.replace("\ufffc", // add part to field specification
p === '[]' ? "[\ufffc]" : "{" + JSON.stringify(p || 'undefined') + ":\ufffc}"
);
});
return field.split("\ufff0").join('$$$');
return field.split("\ufffc").join('$$$');

@@ -728,17 +737,47 @@ },

/**
* Deep merge two plain object heirarchies.
* Does not check for hasOwnProperty.
* Does not deal with cyclical references (at all).
* Concatenates arrays (rather than trying to merge their elements).
* Doesn't guarantee that new cyclical relationships won't be created.
* Doesn't guarantee good behavior when asymentrical types are encountered.
* Deep merge one object into another.
*
* Notes:
* - does not check for hasOwnProperty.
* - does not deal with cyclical references (at all).
* - concatenates arrays (rather than trying to merge their elements).
* - doesn't guarantee that new cyclical relationships won't be created.
* - doesn't guarantee good behavior when asymentrical types are encountered.
*
* @param {mixed} obj The base object to merge into.
* @param {mixed} other The other object to merge into the base.
* @param {mixed} opts Options to use for merge (optional).
*/
merge = corridor.merge = function(obj, other) {
merge = corridor.merge = function(obj, other, opts) {
var i, ii, key;
var
strategy = defaults.merge,
i, ii, key, tmp;
if (toString.call(other) === '[object Array]') {
if (opts && 'merge' in opts) {
strategy = opts.merge;
}
if (arraylike(other)) {
if (toString.call(obj) === '[object Array]') {
for (i = 0, ii = other.length; i < ii; i++) {
obj.push(other[i]);
if (strategy === 'concat') {
for (i = 0, ii = other.length; i < ii; i++) {
obj.push(other[i]);
}
} else if (strategy === 'extend') {
for (i = 0, ii = other.length; i < ii; i++) {
obj[i] = merge(obj[i], other[i], opts);
}
} else {
if (!obj.length || other.length > 1) {
for (i = 0, ii = other.length; i < ii; i++) {
obj.push(other[i]);
}
} else {
if (safely(obj[obj.length - 1], other[0])) {
obj[obj.length - 1] = merge(obj[obj.length - 1], other[0], opts);
} else {
obj.push(other[0]);
}
}
}

@@ -748,3 +787,3 @@ } else {

if (i in obj && typeof obj[i] === 'object' && obj[i] !== null) {
merge(obj[i], other[i]);
obj[i] = merge(obj[i], other[i], opts);
} else {

@@ -756,5 +795,12 @@ obj[i] = other[i];

} else {
if (toString.call(obj) === '[object Array]') {
tmp = {};
for (i = 0, ii = obj.length; i < ii; i++) {
tmp[i] = obj[i];
}
obj = tmp;
}
for (key in other) {
if (key in obj && typeof obj[key] === 'object' && obj[key] !== null) {
merge(obj[key], other[key]);
obj[key] = merge(obj[key], other[key], opts);
} else {

@@ -767,2 +813,88 @@ obj[key] = other[key];

return obj;
},
/**
* Determine whether a candidate object can be safely merged into a base object.
* @param {mixed} obj The base object to test for merge safety.
* @param {mixed} other The candidiate object to check for safe merge.
*/
safely = corridor.safely = function(obj, other) {
var
typeObj = toString.call(obj),
typeOther = toString.call(other),
key;
if (typeObj === '[object Array]') {
if (typeOther === '[object Array]' || typeOther === '[object Object]') {
return true;
}
} else if (typeObj === '[object Object]') {
if (typeOther === '[object Array]') {
return true;
}
if (typeOther === '[object Object]') {
for (key in other) {
if ((key in obj) && !safely(obj[key], other[key])) {
return false;
}
}
return true;
}
}
return false;
},
/**
* Determine whether a given object can be converted to an array without losing data.
* @param {mixed} obj The object to inspect.
* @return {boolean} True if this object could be converted to an array without losing data.
*/
arraylike = corridor.arraylike = function(obj) {
var
type = toString.call(obj),
posInt = /0|[1-9]\d*/,
length,
key,
i;
if (type === '[object Array]') {
return true;
} else if (type !== '[object Object]') {
return false;
}
if (('length' in obj) && !posInt.test(obj.length)) {
return false;
}
length = 0;
for (key in obj) {
if (key !== 'length') {
if (!posInt.test(key)) {
return false;
}
length += 1;
}
}
if (('length' in obj) && obj.length !== length) {
return false;
}
if (length === 0) {
return true;
}
for (i = 0; i < length; i++) {
if (!(i in obj)) {
return false;
}
}
return true;
};

@@ -769,0 +901,0 @@

/**
* test-merge.js - tests the merge() function.
*/
exports['corridor.merge()'] = function(test) {
exports['corridor.merge(objects)'] = function(test) {

@@ -11,2 +11,43 @@ var

suite = [{
obj: {},
other: { b: 'hi' },
expected: { b: 'hi' },
reason: 'all keys should be added to empty objects'
},{
obj: { a: 'whut' },
other: { b: 'hi' },
expected: { a: 'whut', b: 'hi' },
reason: 'missing keys should be added to non-empty objects'
},{
obj: { "person": { "name": "Bob" } },
other: { "person": { "email": "bob@company.com" } },
expected: { "person": { "name": "Bob", "email": "bob@company.com" } },
reason: 'nested objects should safely merge'
},{
obj: { "person": { "name": "Bob" } },
other: { "person": { "name": "Alice" } },
expected: { "person": { "name": "Alice" } },
reason: 'nested conflicting objects should have their keys take precidence'
}];
test.expect(suite.length);
for (var i = 0, ii = suite.length; i < ii; i++) {
(function(data){
var actual = corridor.merge(data.obj, data.other);
test.equals(JSON.stringify(actual), JSON.stringify(data.expected), data.reason);
})(suite[i]);
}
test.done();
};
exports['corridor.merge(arrays)'] = function(test) {
var
corridor = require('../src/corridor.js'),
suite = [{
obj: ['a'],

@@ -18,5 +59,10 @@ other: ['b'],

obj: [{a: 'hi'}],
other: [{a: 'there'}],
expected: [{a: 'hi'}, {a: 'there'}],
reason: 'arrays of conflicting objects should concatenate'
},{
obj: [{a: 'hi'}],
other: [{b: 'there'}],
expected: [{a: 'hi'}, {b: 'there'}],
reason: 'arrays of objects should concatenate'
expected: [{a: 'hi', b: 'there'}],
reason: 'arrays of non-conflicting objects should merge'
},{

@@ -32,12 +78,43 @@ obj: {list: ['hi']},

reason: 'primitves should overwrite each other while arrays concatenate'
}];
test.expect(suite.length);
for (var i = 0, ii = suite.length; i < ii; i++) {
(function(data){
var actual = corridor.merge(data.obj, data.other);
test.equals(JSON.stringify(actual), JSON.stringify(data.expected), data.reason);
})(suite[i]);
}
test.done();
};
exports['corridor.merge(mismatch)'] = function(test) {
var
corridor = require('../src/corridor.js'),
suite = [{
obj: ['a'],
other: {0:'b'},
expected: ['a', 'b'],
reason: 'arraylike objects should behave like arrays for merging'
},{
obj: {},
other: { b: 'hi' },
expected: { b: 'hi' },
reason: 'all keys should be added to empty objects'
obj: {"foo":"bar"},
other: ["baz"],
expected: {"foo":"bar",0:"baz"},
reason: 'an array should contribute numeric keys to an object'
},{
obj: { a: 'whut' },
other: { b: 'hi' },
expected: { a: 'whut', b: 'hi' },
reason: 'missing keys should be added to non-empty objects'
obj: ['baz'],
other: {"foo":"bar"},
expected: {0:"baz","foo":"bar"},
reason: 'merging a non-array-like object into an array should objectify the array'
},{
obj: {"deep":['baz']},
other: {"deep":{"foo":"bar"}},
expected: {"deep":{0:"baz","foo":"bar"}},
reason: 'merging a non-array-like sub-key into an array should objectify the array'
}];

@@ -50,3 +127,3 @@

var actual = corridor.merge(data.obj, data.other);
test.deepEqual(actual, data.expected, data.reason);
test.equals(JSON.stringify(actual), JSON.stringify(data.expected), data.reason);
})(suite[i]);

@@ -59,1 +136,58 @@ }

exports['corridor.merge(concat)'] = function(test) {
var
corridor = require('../src/corridor.js'),
suite = [{
obj: [{a: 'hi'}],
other: [{b: 'there'}],
expected: [{a: 'hi'}, {b: 'there'}],
reason: 'arrays of non-conflicting objects should concatenate in concat mode'
}];
test.expect(suite.length);
for (var i = 0, ii = suite.length; i < ii; i++) {
(function(data){
var actual = corridor.merge(data.obj, data.other, {merge:'concat'});
test.equals(JSON.stringify(actual), JSON.stringify(data.expected), data.reason);
})(suite[i]);
}
test.done();
};
exports['corridor.merge(extend)'] = function(test) {
var
corridor = require('../src/corridor.js'),
suite = [{
obj: [{a: 'hi'}],
other: [{b: 'there'}],
expected: [{a: 'hi', b: 'there'}],
reason: 'arrays of non-conflicting objects should concatenate in extend mode'
},{
obj: [{a: 'hi'}, {b: 'sneak attack!'}],
other: [{b: 'there'}],
expected: [{a: 'hi', b: 'there'}, {b: 'sneak attack!'}],
reason: 'arrays of objects should merge in extend mode'
}];
test.expect(suite.length);
for (var i = 0, ii = suite.length; i < ii; i++) {
(function(data){
var actual = corridor.merge(data.obj, data.other, {merge:'extend'});
test.equals(JSON.stringify(actual), JSON.stringify(data.expected), data.reason);
})(suite[i]);
}
test.done();
};

Sorry, the diff of this file is not supported yet

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