commit c4009dc36496532e43e5dcbf6695e7e981260f66 Author: Geoff Doty Date: Mon Apr 2 00:35:22 2018 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e5a24e --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Record.js + +> A minimalistic dead-simple object collection library + +Grap from `dist`. + +## WIP + +Not done yet! + +## Docs + +- [docs](docs/doc.md) + +## Tests + + npm test + +## TODO +- implement local storage \ No newline at end of file diff --git a/dist/record.min.js b/dist/record.min.js new file mode 100644 index 0000000..a088ea5 --- /dev/null +++ b/dist/record.min.js @@ -0,0 +1,2 @@ +/* Record.js | MIT | https://github.com/n2geoff/record.js */ +;(function(root,factory){if(typeof module==="object"&&module.exports){module.exports=factory(root.Record)}else{root.Record=factory(root.Record)}})(this,function(me){class Record{constructor(init,opts){this.store=(opts||{}).store;this.debug=(opts||{}).debug;this.records=Array.isArray(init)?init:[]}_log(){if(!this.debug){console.log(...arguments)}}add(entry){if(Array.isArray(entry)){entry.forEach(i=>{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);return entry})}else{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);return entry}}find(key){if(!key){return this.records}if(typeof key==="string"||typeof key==="number"){return this.records.filter(record=>{return record.id===key})}let value=Object.keys(key);return this.records.filter(record=>{if(value.indexOf("id")!==-1){return record.id===key.id}return value.every(obj=>{return record[value]===key[value]})})}remove(entry){if(!entry||Array.isArray(entry)){this._log(console.error("remove() accepts a single object"));return[]}let entries=this.find(entry);entries.forEach(item=>{this.records.splice(this.records.indexOf(item),1)});return entries}clear(){this.records=[]}count(){return this.records.length}save(){if(this.storage){}}dump(){}}return Record}); diff --git a/docs/doc.md b/docs/doc.md new file mode 100644 index 0000000..f7bb038 --- /dev/null +++ b/docs/doc.md @@ -0,0 +1,103 @@ +### Record.JS + +- [constructor][1] +- [add][2] +- [find][3] +- [remove][4] +- [count][5] + +## constructor + +**Parameters** + +- `init` **[array][6]** collection to start with +- `opts` **[object][7]** + - `opts.store` **[object][7]** localStorage ID to use + - `opts.debug` **[object][7]** show console logs + +**Examples** + +```javascript +// create a new record (in-memory) +let pets = new Record(); +``` + +## add + +Add record to collection creating an sudo unique id if +one not provided + +**Parameters** + +- `entry` **[Object][7]** object(s) you want to store + +**Examples** + +```javascript +// add pet to collection +let dog = pets.add({"name": "Yonkers", "age": 5}); + +// [{"id": "14rj345k9", "name": "Yonkers", "age": 5}] +``` + +Returns **[array][6]** record added + +## find + +Finds records in collection by id or object filter. + +**Parameters** + +- `key` **([string][8] \| [number][9])** (optional) by id, or object + +**Examples** + +```javascript +// find all +let all = collection.find(); +``` + +```javascript +// find by id +let record = collection.find(1); +``` + +```javascript +// filter by object +let dogs = collection.find({"type": "dog"}); +``` + +Returns **[array][6]** matching records + +## remove + +- **See: [find][10]** + +Remove record(s) from collection. Leverages same functionality as `find` + +**Parameters** + +- `entry` **any** (optional) + +**Examples** + +```javascript +// remove all records by type +let removed = collection.remove({"type": "cat"}); +``` + +Returns **[array][6]** records removed + +## count + +Returns **[number][9]** count of records in collection + +[1]: #constructor + +[2]: #add + +[3]: #find + +[4]: #remove + +[5]: #count diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8cf601d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,307 @@ +{ + "name": "record.js", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "es-abstract": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", + "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "for-each": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.2.tgz", + "integrity": "sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ=", + "dev": true, + "requires": { + "is-function": "1.0.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "1.1.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-function": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.1.tgz", + "integrity": "sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "object-inspect": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.5.0.tgz", + "integrity": "sha512-UmOFbHbwvv+XHj7BerrhVq+knjceBdkvU5AriwLMvhv2qi+e7DJzxfBeFpILEjVzCp+xA+W/pIf06RGPWlZNfw==", + "dev": true + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "string.prototype.trim": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz", + "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.11.0", + "function-bind": "1.1.1" + } + }, + "tape": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.9.0.tgz", + "integrity": "sha512-j0jO9BiScfqtPBb9QmPLL0qvxXMz98xjkMb7x8lKipFlJZwNJkqkWPou+NU4V6T9RnVh1kuSthLE8gLrN8bBfw==", + "dev": true, + "requires": { + "deep-equal": "1.0.1", + "defined": "1.0.0", + "for-each": "0.3.2", + "function-bind": "1.1.1", + "glob": "7.1.2", + "has": "1.0.1", + "inherits": "2.0.3", + "minimist": "1.2.0", + "object-inspect": "1.5.0", + "resolve": "1.5.0", + "resumer": "0.0.0", + "string.prototype.trim": "1.1.2", + "through": "2.3.8" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "uglify-es": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", + "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", + "dev": true, + "requires": { + "commander": "2.13.0", + "source-map": "0.6.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f3969b6 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "record.js", + "version": "0.1.0", + "description": "minimalistic dead-simple storage library", + "main": "src/record.js", + "directories": { + "doc": "docs", + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node_modules/.bin/tape test/*.spec.js" + }, + "keywords": [ + "collection", + "storage", + "local", + "memory", + "minimalist" + ], + "author": "Geoff Doty", + "license": "MIT", + "devDependencies": { + "tape": "^4.9.0", + "uglify-es": "^3.3.9" + } +} diff --git a/src/record.js b/src/record.js new file mode 100644 index 0000000..0bea53c --- /dev/null +++ b/src/record.js @@ -0,0 +1,191 @@ +'use strict'; + +(function(root, factory) { + if(typeof module === "object" && module.exports) { + module.exports = factory(root.Record); + } else { + root.Record = factory(root.Record); + } + +}(this, function(me) { + + /* + * Record.js + * + * A dead-simple object collection library + */ + class Record { + /** + * @constructor + * + * @example + * // create a new record (in-memory) + * let pets = new Record(); + * + * @param {array} init - collection to start with + * @param {object} opts + * @param {object} opts.store - localStorage ID to use + * @param {object} opts.debug - show console logs + */ + constructor(init, opts) { + // define options + this.store = (opts || {}).store; + this.debug = (opts || {}).debug; + + // initialize the collection + this.records = Array.isArray(init) ? init : []; + } + + /** + * Supresses logs based on this.debug value + * + * @private + */ + _log() { + if(!this.debug) { + console.log(...arguments); + } + } + + /** + * Add record to collection creating an sudo unique id if + * one not provided + * + * @example + * // add pet to collection + * let dog = pets.add({"name": "Yonkers", "age": 5}); + * // > [{"id": "14rj345k9", "name": "Yonkers", "age": 5}] + * + * @param {Object} entry - object(s) you want to store + * @returns {object} entry added + */ + add(entry) { + + if(Array.isArray(entry)) { + entry.forEach((i) => { + if(!entry.id) { + entry.id = Math.random().toString(36).substr(2, 9); + } + + this.records.push(entry); + + return entry; + }); + } else { + if(!entry.id) { + entry.id = Math.random().toString(36).substr(2, 9); + } + + this.records.push(entry); + + return entry; + } + } + + /** + * Finds records in collection by id or object filter. + * + * @example + * // find all + * let all = collection.find(); + * + * @example + * // find by id + * let record = collection.find(1); + * + * @example + * // filter by object + * let dogs = collection.find({"type": "dog"}); + * + * @param {string|number} key - (optional) by id, or object + * @returns {array} matching records + */ + find(key) { + + // return all records + if(!key) { + return this.records; + } + + // find by id + if(typeof key === 'string' || typeof key === 'number') { + return this.records.filter((record) => { + return record.id === key; + }); + } + + // filter by + let value = Object.keys(key); + + // filter records for value + return this.records.filter((record) => { + + // id trumps all + if(value.indexOf('id') !== -1) { + return record.id === key.id; + } + + return value.every((obj) => { + return record[value] === key[value]; + }); + }); + + } + + /** + * Remove record(s) from collection. Leverages same functionality as `find` + * + * @example + * // remove all records by type + * let removed = collection.remove({"type": "cat"}); + * // > [] + * + * @param {any} entry - (optional) + * + * @returns {array} records removed + */ + remove(entry) { + + // use clear() to remove all + if(!entry || Array.isArray(entry)) { + this._log(console.error('remove() accepts a single object')); + return []; + } + + // find matching records + let entries = this.find(entry); + + // remove all matching records + entries.forEach((item) => { + this.records.splice(this.records.indexOf(item), 1); + }); + + // return records removed + return entries; + } + + clear() { + this.records = []; + } + + /** + * @returns {number} count of records in collection + */ + count() { + return this.records.length; + } + + // save to localstorage + save() { + if(this.storage) { + + } + } + + dump() {} + + } + + return Record; +})); + \ No newline at end of file diff --git a/test/record.spec.js b/test/record.spec.js new file mode 100644 index 0000000..1f6d970 --- /dev/null +++ b/test/record.spec.js @@ -0,0 +1,56 @@ +const test = require('tape'); +const Record = require('../src/record.js'); + +test('Record.js', function(t) { + let pets; + + t.test('setup', function(t) { + pets = new Record(); + + t.ok(Record, 'Record should exist'); + t.ok(pets, 'new collection should have been created'); + t.end(); + }); + + t.test('should add 3 records one-by-one', function(t) { + t.ok(pets.add, 'add method exists'); + + let plato = pets.add({"name": "plato", "type": "dog"}); + let socrates = pets.add({"id": "1", "name": "socrates", "type": "dog"}); + let hypatia = pets.add({"name": "hypatia", "type": "cat"}); + + t.equal(plato.name, 'plato', 'plato should have been added'); + t.equal(socrates.name, 'socrates', 'socrates should have been added'); + t.equal(hypatia.name, 'hypatia', 'hypatia should have been added'); + + t.ok(plato.id, 'platos record id should be auto-generated'); + t.equal(socrates.id, "1",'socrates record id should not be auto-generated'); + + t.end(); + }); + + t.test('should be 3 records', function(t) { + t.equal(pets.count(), 3); + t.end(); + }); + + t.test("should find all matching records", function(t) { + let dogs = pets.find({"type": "dog"}); + + t.equal(dogs.length, 2, "should be 2 dogs in the house"); + t.equal(pets.count(), 3, "but should still have 3 pets"); + + t.end(); + }); + + t.test('should be able to remove a record', function() { + let hypatia = pets.remove({"name": "hypatia"}); + + t.equal(hypatia[0].name, 'hypatia', 'hypatia should be removed'); + + t.equal(pets.count(), 2, 'yes, hypatia has left the building'); + + t.end(); + }); + +}); \ No newline at end of file