From 857819191ce201f78de8abc8245c3e126ad14b02 Mon Sep 17 00:00:00 2001 From: atticus Date: Fri, 2 May 2014 19:53:35 -0400 Subject: [PATCH] filter can now be used as a replacement to Model#toJSON() a tweak and a test prevent crash on undefined val --- lib/filtered.js | 16 ++++++++++--- lib/utils/clone.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++ lib/utils/type.js | 35 +++++++++++++++++++++++++++ test/filtered.js | 16 +++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 lib/utils/clone.js create mode 100644 lib/utils/type.js diff --git a/lib/filtered.js b/lib/filtered.js index 40bcde4..79dd3f7 100644 --- a/lib/filtered.js +++ b/lib/filtered.js @@ -1,3 +1,7 @@ +'use strict'; + +var clone = require('./utils/clone'); + module.exports = function(Model) { var filteredAttrs = []; @@ -9,7 +13,7 @@ module.exports = function(Model) { }); Model.prototype.filter = function(filterList) { - if(typeof filterList == 'string') { + if(typeof filterList === 'string') { filterList = [filterList]; } @@ -19,10 +23,16 @@ module.exports = function(Model) { filterList = filterList.concat(filteredAttrs); - var attrs = this.toJSON(); + var attrs = {}; + var self = this; + + Object.keys(this.attrs).forEach(function (key) { + var val = self.attrs[key]; + attrs[key] = (val && val.toJSON) ? val.toJSON() : clone(val); + }); for(var i = 0; i < filterList.length; ++i) { - delete attrs[filterList[i]]; + delete attrs[filterList[i]]; } return attrs; diff --git a/lib/utils/clone.js b/lib/utils/clone.js new file mode 100644 index 0000000..4b407b6 --- /dev/null +++ b/lib/utils/clone.js @@ -0,0 +1,59 @@ +/** + * TODO: cleanup. This is a pretty big hack. inlined `clone` because + * it cannot handle objectid instances + */ + +/** + * Module dependencies + */ + +var type = require('./type'); + +/** + * Expose `clone` + */ + +module.exports = clone; + +/** + * Clone values. + * + * @param {Mixed} val + * @return {Mixed} + * @api public + */ + +function clone(obj) { + switch (type(obj)) { + case 'object': + // Hack for BSON IDs + if(obj.toHexString) + return obj; + var copy = {}; + for (var key in obj) { + copy[key] = clone(obj[key]); + } + return copy; + + case 'array': + var copy = new Array(obj.length); + for (var i = 0, l = obj.length; i < l; i++) { + copy[i] = clone(obj[i]); + } + return copy; + + case 'regexp': + // from millermedeiros/amd-utils - MIT + var flags = ''; + flags += obj.multiline ? 'm' : ''; + flags += obj.global ? 'g' : ''; + flags += obj.ignoreCase ? 'i' : ''; + return new RegExp(obj.source, flags); + + case 'date': + return new Date(obj.getTime()); + + default: // string, number, boolean, … + return obj; + } +} diff --git a/lib/utils/type.js b/lib/utils/type.js new file mode 100644 index 0000000..ee8d9c6 --- /dev/null +++ b/lib/utils/type.js @@ -0,0 +1,35 @@ +/** + * https://github.com/component/type + * + * TODO: un-bundle, once we have a way to use component and node together + */ + +/** + * toString ref. + */ + +var toString = Object.prototype.toString; + +/** + * Return the type of `val`. + * + * @param {Mixed} val + * @return {String} + * @api public + */ + +module.exports = function(val){ + switch (toString.call(val)) { + case '[object Function]': return 'function'; + case '[object Date]': return 'date'; + case '[object RegExp]': return 'regexp'; + case '[object Arguments]': return 'arguments'; + case '[object Array]': return 'array'; + } + + if (val === null) return 'null'; + if (val === undefined) return 'undefined'; + if (val === Object(val)) return 'object'; + + return typeof val; +}; diff --git a/test/filtered.js b/test/filtered.js index 6b6cf78..c55c993 100644 --- a/test/filtered.js +++ b/test/filtered.js @@ -32,4 +32,20 @@ describe("Filtered", function() { it("auto-filters properties defined with filter flag", function() { expect(user.filter()).to.not.have.key('hashedPassword'); }); + + it("can be used inside model#toJSON()", function() { + User.prototype.toJSON = function() { + return this.filter; + }; + var user = new User({ + username: 'thisisauser', + hashedPassword: '129408158932dsjkaklfjsad', + socialSecurityNumber: '123-456-7890' + }); + var result = user.filter('socialSecurityNumber'); + + expect(result).to.not.have.key('socialSecurityNumber'); + expect(result).to.have.key('username'); + + }); });