Skip to content

Commit

Permalink
Merge pull request #48 from canjs/add-dot-notation-prop
Browse files Browse the repository at this point in the history
Adding dotNotation property and tests
  • Loading branch information
nlundquist committed Apr 13, 2017
2 parents 5e5b1dd + dd8deeb commit cd36b8e
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
94 changes: 94 additions & 0 deletions src/prop_tests/dotNotation_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
var QUnit = require("steal-qunit");

var set = require('../set-core'),
props = require("../props");

QUnit.module("can-set props.dotNotation");

/*
* For the dotNotation prop, we define sets like so:
*
* For a property 'n.p', with value 'IL'
* x ∈ X | x.n.p = 'IL'
*
*/
test('dotNotation set membership', function() {
/*
* For a property 'n.p', with value 'IL'
* x ∈ X | x.n.p == 'IL'
*/
var prop = props.dotNotation('n.p'),
alg = new set.Algebra(prop),
res = alg.has({'n.p': 'IL'}, {n:{p:'IL'}});
ok(res, "object with nested property is member of set using dotNotation");

/*
* For a property 'n.p', with value 'IL'
* x ∉ X | x.n.p != 'IL'
*/
res = alg.has({'n.p': 'IL'}, {n:{p:'MI'}});
ok(res === false, "object with nested property not a member of set using dotNotation");

/*
* For a property 'n.p.s', with value 'IL'
* x ∈ X | x.n.p.s == 'IL'
*/
prop = props.dotNotation('n.p.s');
alg = new set.Algebra(prop);
res = alg.has({'n.p.s': 'IL'}, {n:{p:{s:'IL'}}});
ok(res, "object with deep nested property is member of set using dotNotation");
});

test('dotNotation set equality', function() {
var prop = props.dotNotation('n.p'),
alg = new set.Algebra(prop),
set1 = {'n.p': 'IL'},
set2 = {'n.p': 'IL'},
set3 = {'n.p': 'MI'},
set4 = {n:{p:'MI'}};

/*
* {x | x ∈ X, x.n.p == 'IL'} = {y | y ∈ Y, y.n.p == 'IL'}
*/
ok(alg.equal(set1, set2) && alg.equal(set2, set1), "sets with dotNotation properties are equivalent");

/*
* {x | x ∈ X, x.n.p == 'IL'} != {y | y ∈ Y, y.n.p == 'MI'}
*/
ok(alg.equal(set1, set3) === false, "sets with dotNotation properties are not equivalent");

/*
* {x | x ∈ X, x.n.p == 'MI'} = {y | y ∈ Y, y.n.p == 'MI'}
*/
ok(alg.equal(set4, set3) === false, "sets with dotNotation properties are equivalent to sets with nested properties");
});

test('dotNotation set subset', function() {
var alg = new set.Algebra(
props.dotNotation('address.state'),
props.dotNotation('address.city')
),
set1 = {'address.state': 'IL'},
set2 = {'address.state': 'IL', 'address.city': 'Chicago'},
set3 = {address: {state: 'IL', city: 'Chicago'}};

/*
* {x | x ∈ X, x.address.state = 'IL', x.address.city = 'Chicago'} ⊆ {y | y ∈ Y, y.address.state == 'IL'}
*/
ok(alg.subset(set2, set1), "sets with dotNotation property is a subset of another dotNotation set");

/*
* {x | x ∈ X, x.address.state = 'IL', x.address.city = 'Chicago'} ⊆ {y | y ∈ Y, y.address.state == 'IL'}
*/
ok(alg.subset(set3, set1), "sets with nested property notation is a subset of a dotNotation set");

/*
* {y | y ∈ Y, y.address.state == 'IL'} ⊆ ξ
*/
ok(alg.subset(set1, {}), "sets with dotNotation properties are subsets of the universal set");

/*
* ξ ⊄ {y | y ∈ Y, y.address.state == 'IL'}
*/
ok(alg.subset({}, set1) === false, "the universal set is not a subset of a set with dotNotation");
});
47 changes: 47 additions & 0 deletions src/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,51 @@ props.rangeInclusive = function(startIndexProperty, endIndexProperty){
});
};

var nestedLookup = function(obj, propNameArray) {
if (obj === undefined) {
return undefined;
}

if (propNameArray.length === 1) {
return obj[propNameArray[0]];
} else {
return nestedLookup(obj[propNameArray[0]], propNameArray.slice(1));
}
};
/**
* @function can-set.props.dotNotation dotNotation
* @parent can-set.props
*
* @description Supports MongoDB-style 'dot notation' properties.
*
* @signature `set.props.dotNotation(dotProperty)`
*
* Defines a property that specifies a MongoDB-style nested property match.
* For example, a set property of "address.city" matches against the value of the nested `{address: {city}}` value.
*
* ```
* set.props.dotNotation("address.city")
* ```
*
* @param {String} dotProperty The MongoDB-style nested property name
* @return {can-set.compares} Returns a comparator used to build a set algebra
*/
props.dotNotation = function(dotProperty){
var compares = new clause.Where({});

compares[dotProperty] = function(aVal, bVal, a, b, propertyName) {
// if the value wasn't picked out from the parent try a dotNotation lookup
if (aVal === undefined) {
aVal = nestedLookup(a, propertyName.split('.'));
}
if (bVal === undefined) {
bVal = nestedLookup(b, propertyName.split('.'));
}

return aVal === bVal;
};

return compares;
};

module.exports = props;
1 change: 1 addition & 0 deletions src/props_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ require('./prop_tests/rangeInclusive_test');
require('./prop_tests/offsetLimit_test');
require('./prop_tests/boolean_test');
require('./prop_tests/enum_test');
require('./prop_tests/dotNotation_test');

0 comments on commit cd36b8e

Please sign in to comment.