Skip to content

Commit

Permalink
add support for additional reactive inputs to model.filter
Browse files Browse the repository at this point in the history
  • Loading branch information
nateps committed Oct 31, 2014
1 parent 08f548b commit a87f347
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 56 deletions.
131 changes: 80 additions & 51 deletions lib/Model/filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,57 @@ Model.INITS.push(function(model) {
for (var path in map) {
var filter = map[path];
if (pass.$filter === filter) continue;
if (util.mayImpact(filter.inputSegments, segments)) {
if (
util.mayImpact(filter.segments, segments) ||
(filter.inputsSegments && util.mayImpactAny(filter.inputsSegments, segments))
) {
filter.update(pass);
}
}
}
});

Model.prototype.filter = function() {
var input, options, fn;
if (arguments.length === 1) {
fn = arguments[0];
} else if (arguments.length === 2) {
if (this.isPath(arguments[0])) {
input = arguments[0];
} else {
options = arguments[0];
}
fn = arguments[1];
} else {
input = arguments[0];
options = arguments[1];
fn = arguments[2];
function parseFilterArguments(model, args) {
var fn = args.pop();
var path = model.path(args.shift());
var options;
if (!model.isPath(args[args.length - 1])) {
options = args.pop();
}
var i = args.length;
while (i--) {
args[i] = model.path(args[i]);
}
var inputPath = this.path(input);
return this.root._filters.add(inputPath, fn, null, options);
return {
path: path,
inputPaths: (args.length) ? args : null,
options: options,
fn: fn
};
}

Model.prototype.filter = function() {
var args = Array.prototype.slice.call(arguments);
var parsed = parseFilterArguments(this, args);
return this.root._filters.add(
parsed.path,
parsed.fn,
null,
parsed.inputPaths,
parsed.options
);
};

Model.prototype.sort = function() {
var input, options, fn;
if (arguments.length === 1) {
fn = arguments[0];
} else if (arguments.length === 2) {
if (this.isPath(arguments[0])) {
input = arguments[0];
} else {
options = arguments[0];
}
fn = arguments[1];
} else {
input = arguments[0];
options = arguments[1];
fn = arguments[2];
}
if (!fn) throw new TypeError('Sort function is required');
var inputPath = this.path(input);
return this.root._filters.add(inputPath, null, fn, options);
var args = Array.prototype.slice.call(arguments);
var parsed = parseFilterArguments(this, args);
return this.root._filters.add(
parsed.path,
null,
parsed.fn || 'asc',
parsed.inputPaths,
parsed.options
);
};

Model.prototype.removeAllFilters = function(subpath) {
Expand All @@ -78,8 +83,8 @@ function Filters(model) {
this.fromMap = new FromMap();
}

Filters.prototype.add = function(inputPath, filterFn, sortFn, options) {
return new Filter(this, inputPath, filterFn, sortFn, options);
Filters.prototype.add = function(path, filterFn, sortFn, inputPaths, options) {
return new Filter(this, path, filterFn, sortFn, inputPaths, options);
};

Filters.prototype.toJSON = function() {
Expand All @@ -88,23 +93,32 @@ Filters.prototype.toJSON = function() {
var filter = this.fromMap[from];
// Don't try to bundle if functions were passed directly instead of by name
if (!filter.bundle) continue;
var args = [from, filter.inputPath, filter.filterName, filter.sortName];
var args = [from, filter.path, filter.filterName, filter.sortName, filter.inputPaths];
if (filter.options) args.push(filter.options);
out.push(args);
}
return out;
};

function Filter(filters, inputPath, filterFn, sortFn, options) {
function Filter(filters, path, filterFn, sortFn, inputPaths, options) {
this.filters = filters;
this.model = filters.model.pass({$filter: this});
this.inputPath = inputPath;
this.inputSegments = inputPath.split('.');
this.path = path;
this.segments = path.split('.');
this.filterName = null;
this.sortName = null;
this.bundle = true;
this.filterFn = null;
this.sortFn = null;
this.inputPaths = inputPaths;
this.inputsSegments = null;
if (inputPaths) {
this.inputsSegments = [];
for (var i = 0; i < this.inputPaths.length; i++) {
var segments = this.inputPaths[i].split('.');
this.inputsSegments.push(segments);
}
}
this.options = options;
this.skip = options && options.skip;
this.limit = options && options.limit;
Expand Down Expand Up @@ -155,18 +169,34 @@ Filter.prototype._slice = function(results) {
return results.slice(begin, end);
};

Filter.prototype.getInputs = function() {
if (!this.inputsSegments) return;
var inputs = [];
for (var i = 0, len = this.inputsSegments.length; i < len; i++) {
var input = this.model._get(this.inputsSegments[i]);
inputs.push(input);
}
return inputs;
};

Filter.prototype.callFilter = function(items, key, inputs) {
var item = items[key];
return (inputs) ?
this.filterFn.apply(this.model, [item, key, items].concat(inputs)) :
this.filterFn.call(this.model, item, key, items);
};

Filter.prototype.ids = function() {
var items = this.model._get(this.inputSegments);
var items = this.model._get(this.segments);
var ids = [];
if (!items) return ids;
if (Array.isArray(items)) {
throw new Error('model.filter is not currently supported on arrays');
}
if (this.filterFn) {
var inputs = this.getInputs();
for (var key in items) {
if (items.hasOwnProperty(key) &&
this.filterFn.call(this.model, items[key], key, items)
) {
if (items.hasOwnProperty(key) && this.callFilter(items, key, inputs)) {
ids.push(key);
}
}
Expand All @@ -183,16 +213,15 @@ Filter.prototype.ids = function() {
};

Filter.prototype.get = function() {
var items = this.model._get(this.inputSegments);
var items = this.model._get(this.segments);
var results = [];
if (Array.isArray(items)) {
throw new Error('model.filter is not currently supported on arrays');
}
if (this.filterFn) {
var inputs = this.getInputs();
for (var key in items) {
if (items.hasOwnProperty(key) &&
this.filterFn.call(this.model, items[key], key, items)
) {
if (items.hasOwnProperty(key) && this.callFilter(items, key, inputs)) {
results.push(items[key]);
}
}
Expand All @@ -219,7 +248,7 @@ Filter.prototype.ref = function(from) {
this.filters.fromMap[from] = this;
this.idsSegments = ['$filters', from.replace(/\./g, '|')];
this.update();
return this.model.refList(from, this.inputPath, this.idsSegments.join('.'));
return this.model.refList(from, this.path, this.idsSegments.join('.'));
};

Filter.prototype.destroy = function() {
Expand Down
6 changes: 2 additions & 4 deletions lib/Model/fn.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,11 @@ function parseStartArguments(model, args, hasPath) {
if (hasPath) {
path = model.path(args.shift());
}
var i = args.length - 1;
var options;
if (model.isPath(args[i])) {
args[i] = model.path(args[i]);
} else {
if (!model.isPath(args[args.length - 1])) {
options = args.pop();
}
var i = args.length;
while (i--) {
args[i] = model.path(args[i]);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Model/unbundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Model.prototype.unbundle = function(data) {
// Re-create filters
for (var i = 0; i < data.filters.length; i++) {
var item = data.filters[i];
var filter = this._filters.add(item[1], item[2], item[3], item[4]);
var filter = this._filters.add(item[1], item[2], item[3], item[4], item[5]);
filter.ref(item[0]);
}
};
17 changes: 17 additions & 0 deletions test/Model/filter.mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,22 @@ describe('filter', function() {
}
]);
});
it('supports additional dynamic inputs', function() {
var model = (new Model).at('_page');
var numbers = [0, 3, 4, 1, 2, 3, 0];
for (var i = 0; i < numbers.length; i++) {
model.set('numbers.' + model.id(), numbers[i]);
}
model.set('mod', 3);
model.set('offset', 0);
var filter = model.filter('numbers', 'mod', 'offset', function(number, id, numbers, mod, offset) {
return (number % mod) === offset;
});
expect(filter.get()).to.eql([0, 3, 3, 0]);
model.set('offset', 1);
expect(filter.get()).to.eql([4, 1]);
model.set('mod', 2);
expect(filter.get()).to.eql([3, 1, 3]);
});
});
});

0 comments on commit a87f347

Please sign in to comment.