Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
|
fc5f7e0621 | |
|
9262b28795 | |
|
4e65f51b14 | |
|
b9b45d919c | |
|
3ea896f94f | |
|
c39dba5335 | |
|
faa2ea8f34 | |
|
d2875d4e84 | |
|
886fca439b | |
|
0c11b4af7f | |
|
f84aa6ae45 | |
|
654e39b9a1 | |
|
cdd5647c61 |
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2017,
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"experimentalObjectRestSpread": true
|
||||||
|
},
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
4,
|
||||||
|
{"SwitchCase": 1}
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-extra-semi": [
|
||||||
|
"off",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
44
README.md
44
README.md
|
@ -2,17 +2,21 @@
|
||||||
|
|
||||||
> A minimalistic object collection library
|
> A minimalistic object collection library
|
||||||
|
|
||||||
**Record.js** aims to provide a lite, *2kb*, *zero-dependency* collection utility to help build simple in-memory or local storage database of records. Records are stored as simple arrays.
|
**Record.js** aims to provide a lite, *2kb*, *zero-dependency* collection utility to help build simple in-memory or local storage database of records. Records are stored as plain arrays.
|
||||||
|
|
||||||
Records can be exported to JSON, with no internals so you can import your data anywhere; via `.dump()`
|
Records can be exported to JSON, with no internal metadata[*], so you can import your data anywhere; via `.dump()`
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Tiny, only 2kb
|
- Tiny, only >2kb
|
||||||
- Zero-Dependencies
|
- Zero-Dependencies
|
||||||
- Saves to LocalStorage (if available)
|
- Saves to LocalStorage (if available)
|
||||||
- Saves to simple JSON
|
- Data saved, and exported as simple JSON
|
||||||
- Browser or Nodejs compatible
|
- Browser ESM or Nodejs (use `require('record.cjs.js').default`)
|
||||||
|
|
||||||
|
### Why
|
||||||
|
|
||||||
|
Sometimes having a simple single-user data storage library that stays out of your way while you prototype out your ideas is all you need. With a small footprint, removing/replacing a library like `record.js` is a breeze. Ideally, using this to library to build out configuration files, mock out test data, or even develop simple applications is where this shines. I use it for just that! I often replace this library once an idea becomes soluble; I export the data to JSON to a real DB and expand.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
@ -38,6 +42,13 @@ you can also find items via properties like
|
||||||
pets.find({"type": "cat"});
|
pets.find({"type": "cat"});
|
||||||
// [{"id": "123fk91j7", "name": "Fluffy", "type": "cat"}]
|
// [{"id": "123fk91j7", "name": "Fluffy", "type": "cat"}]
|
||||||
|
|
||||||
|
and when it comes time to update you can do that to
|
||||||
|
|
||||||
|
var fluffy = pets.find({"name": "Fluffy"})[0]; // one record
|
||||||
|
fluffy.age = 25;
|
||||||
|
|
||||||
|
pets.update(fluffy);
|
||||||
|
|
||||||
and you can remove items via
|
and you can remove items via
|
||||||
|
|
||||||
pets.remove("123fk91j7");
|
pets.remove("123fk91j7");
|
||||||
|
@ -50,13 +61,14 @@ so, no records, right?
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
The public API is very simple, you really only need 3 methods: `add`, `remove`, and `find`.
|
The public API is very simple, you really only need 4 methods: `add`, `update`, `remove`, and `find`.
|
||||||
|
|
||||||
| Method | Description |
|
| Method | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `.add(object)` | Adds entry to collection and returns entry(s) added |
|
| `.add(object)` | Adds entry to collection and returns entry(s) added |
|
||||||
| `.remove(id|object)` | Removes entry(s) from collection and returns removed |
|
| `.update(object)` | Updates a record |
|
||||||
| `.find(id|object)` | find all, find by id, or find by filter, returns array of entries |
|
| `.remove(id or object)` | Removes entry(s) from collection and returns removed |
|
||||||
|
| `.find(id or object)` | find all, find by id, or find by filter, returns array of entries |
|
||||||
| `.dump()` | saves records to JSON file |
|
| `.dump()` | saves records to JSON file |
|
||||||
|
|
||||||
#### Length (Count Records)
|
#### Length (Count Records)
|
||||||
|
@ -67,7 +79,7 @@ let cats = pets.find({"type": "cat"}); // []
|
||||||
cats.length; // 0
|
cats.length; // 0
|
||||||
```
|
```
|
||||||
|
|
||||||
> NOTICE: `find` is special, changes based on what you pass in, an id, an object, or nothing at all
|
> NOTICE: `find` is special, changes based on what you pass in, an id, an object, or nothing at all
|
||||||
- Additional [API Documentation](docs/api.md)
|
- Additional [API Documentation](docs/api.md)
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
@ -83,6 +95,12 @@ Records.js constructor supports a few options passed in as an `object`
|
||||||
|
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
npm install esbuild -g
|
||||||
|
|
||||||
|
npm run build
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Please open [an issue](https://github.com/n2geoff/record.js/issues/new) for support.
|
Please open [an issue](https://github.com/n2geoff/record.js/issues/new) for support.
|
||||||
|
@ -94,3 +112,11 @@ Anyone is welcome to contribute, however, if you decide to get involved, please
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> [*]: with exception of a generated ID, if not provided
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- make reactive, so just doing `fluffy.age = 25` updates
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
var __defProp=Object.defineProperty;var __markAsModule=target=>__defProp(target,"__esModule",{value:true});var __export=(target,all)=>{__markAsModule(target);for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};__export(exports,{default:()=>record_default});/*! Record.js v0.8.0 | MIT | https://github.com/n2geoff/record.js */class Record{constructor(opts){this.store=(opts||{}).store;this.debug=(opts||{}).debug||false;this.records=[];if(this.store&&localStorage){this._log("Initializing localStorage for "+this.store);let existing=this._load()||[];this.records=[...existing]}}_log(){if(this.debug){console.log(...arguments)}}add(entry){if(Array.isArray(entry)){let entries=[];entry.forEach(()=>{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);this.entries.push(entry)});this._save();return entries}else{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);this._save();return entry}}update(entry){var idx=this.records.indexOf(entry);if(this.records[idx].id===entry.id){this.records.splice(idx,1,entry);this._save();return entry}else{return false}}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(val=>{return record[val]===key[val]})})}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)});this._save();return entries}clear(){this.records=[];this._save()}_save(){if(this.store&&localStorage){localStorage.setItem(this.store,JSON.stringify(this.records))}}_load(){if(this.store&&localStorage){return JSON.parse(localStorage.getItem(this.store))||[]}}dump(){function download(filename,content){let a=document.createElement("a");let file=new Blob([content],{type:"text/plain"});a.href=URL.createObjectURL(file);a.download=filename;a.click()}download(`${this.store||"data"}.json`,JSON.stringify(this._load(),null,4))}}var record_default=Record;
|
|
@ -1,142 +1 @@
|
||||||
/*! Record.js v0.6.1 | MIT | https://github.com/n2geoff/record.js */
|
/*! Record.js v0.8.0 | MIT | https://github.com/n2geoff/record.js */class Record{constructor(opts){this.store=(opts||{}).store;this.debug=(opts||{}).debug||false;this.records=[];if(this.store&&localStorage){this._log("Initializing localStorage for "+this.store);let existing=this._load()||[];this.records=[...existing]}}_log(){if(this.debug){console.log(...arguments)}}add(entry){if(Array.isArray(entry)){let entries=[];entry.forEach(()=>{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);this.entries.push(entry)});this._save();return entries}else{if(!entry.id){entry.id=Math.random().toString(36).substr(2,9)}this.records.push(entry);this._save();return entry}}update(entry){var idx=this.records.indexOf(entry);if(this.records[idx].id===entry.id){this.records.splice(idx,1,entry);this._save();return entry}else{return false}}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(val=>{return record[val]===key[val]})})}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)});this._save();return entries}clear(){this.records=[];this._save()}_save(){if(this.store&&localStorage){localStorage.setItem(this.store,JSON.stringify(this.records))}}_load(){if(this.store&&localStorage){return JSON.parse(localStorage.getItem(this.store))||[]}}dump(){function download(filename,content){let a=document.createElement("a");let file=new Blob([content],{type:"text/plain"});a.href=URL.createObjectURL(file);a.download=filename;a.click()}download(`${this.store||"data"}.json`,JSON.stringify(this._load(),null,4))}}var record_default=Record;export{record_default as default};
|
||||||
(function (root, factory) {
|
|
||||||
"use strict";
|
|
||||||
if (typeof module === "object" && module.exports) {
|
|
||||||
module.exports = factory(root.Record);
|
|
||||||
} else {
|
|
||||||
root.Record = factory(root.Record);
|
|
||||||
}
|
|
||||||
|
|
||||||
}(this, function () {
|
|
||||||
"use strict";
|
|
||||||
class Record {
|
|
||||||
constructor(opts) {
|
|
||||||
this.store = (opts || {}).store;
|
|
||||||
this.debug = (opts || {}).debug || false;
|
|
||||||
|
|
||||||
this.records = [];
|
|
||||||
|
|
||||||
if(this.store && localStorage) {
|
|
||||||
this._log("Initializing localStorage for " + this.store);
|
|
||||||
|
|
||||||
let existing = this._load() || [];
|
|
||||||
this.records = [...existing];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_log() {
|
|
||||||
if(this.debug) {
|
|
||||||
console.log(...arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add(entry) {
|
|
||||||
|
|
||||||
if (Array.isArray(entry)) {
|
|
||||||
let entries = [];
|
|
||||||
|
|
||||||
entry.forEach(() => {
|
|
||||||
if (!entry.id) {
|
|
||||||
entry.id = Math.random().toString(36).substr(2, 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.records.push(entry);
|
|
||||||
|
|
||||||
this.entries.push(entry);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._save();
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (!entry.id) {
|
|
||||||
entry.id = Math.random().toString(36).substr(2, 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.records.push(entry);
|
|
||||||
|
|
||||||
this._save();
|
|
||||||
|
|
||||||
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((val) => {
|
|
||||||
return record[val] === key[val];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
this._save();
|
|
||||||
|
|
||||||
return entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.records = [];
|
|
||||||
|
|
||||||
this._save();
|
|
||||||
}
|
|
||||||
|
|
||||||
_save() {
|
|
||||||
if (this.store && localStorage) {
|
|
||||||
localStorage.setItem(this.store, JSON.stringify(this.records));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_load() {
|
|
||||||
if (this.store && localStorage) {
|
|
||||||
return JSON.parse(localStorage.getItem(this.store)) || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dump() {
|
|
||||||
function download(filename, content) {
|
|
||||||
let a = document.createElement("a");
|
|
||||||
let file = new Blob([content], {type: "text/plain"});
|
|
||||||
a.href = URL.createObjectURL(file);
|
|
||||||
a.download = filename;
|
|
||||||
a.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
download(`${this.store || "data"}.json`, JSON.stringify(this._load(), null, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Record;
|
|
||||||
}));
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
/*! Record.js v0.6.1 | MIT | https://github.com/n2geoff/record.js */
|
/*! Record.js v0.8.0 | MIT | https://github.com/n2geoff/record.js */class o{constructor(e){if(this.store=(e||{}).store,this.debug=(e||{}).debug||!1,this.records=[],this.store&&localStorage){this._log("Initializing localStorage for "+this.store);let r=this._load()||[];this.records=[...r]}}_log(){this.debug&&console.log(...arguments)}add(e){if(Array.isArray(e)){let r=[];return e.forEach(()=>{e.id||(e.id=Math.random().toString(36).substr(2,9)),this.records.push(e),this.entries.push(e)}),this._save(),r}else return e.id||(e.id=Math.random().toString(36).substr(2,9)),this.records.push(e),this._save(),e}update(e){var r=this.records.indexOf(e);return this.records[r].id===e.id?(this.records.splice(r,1,e),this._save(),e):!1}find(e){if(!e)return this.records;if(typeof e=="string"||typeof e=="number")return this.records.filter(t=>t.id===e);let r=Object.keys(e);return this.records.filter(t=>r.indexOf("id")!==-1?t.id===e.id:r.every(s=>t[s]===e[s]))}remove(e){if(!e||Array.isArray(e))return this._log(console.error("remove() accepts a single object")),[];let r=this.find(e);return r.forEach(t=>{this.records.splice(this.records.indexOf(t),1)}),this._save(),r}clear(){this.records=[],this._save()}_save(){this.store&&localStorage&&localStorage.setItem(this.store,JSON.stringify(this.records))}_load(){if(this.store&&localStorage)return JSON.parse(localStorage.getItem(this.store))||[]}dump(){function e(r,t){let s=document.createElement("a"),i=new Blob([t],{type:"text/plain"});s.href=URL.createObjectURL(i),s.download=r,s.click()}e(`${this.store||"data"}.json`,JSON.stringify(this._load(),null,4))}}var a=o;export{a as default};
|
||||||
!function(t,e){"use strict";"object"==typeof module&&module.exports?module.exports=e(t.Record):t.Record=e(t.Record)}(this,function(){"use strict";return class{constructor(t){if(this.store=(t||{}).store,this.debug=(t||{}).debug||!1,this.records=[],this.store&&localStorage){this._log("Initializing localStorage for "+this.store);let t=this._load()||[];this.records=[...t]}}_log(){this.debug&&console.log(...arguments)}add(t){if(Array.isArray(t)){let e=[];return t.forEach(()=>{t.id||(t.id=Math.random().toString(36).substr(2,9)),this.records.push(t),this.entries.push(t)}),this._save(),e}return t.id||(t.id=Math.random().toString(36).substr(2,9)),this.records.push(t),this._save(),t}find(t){if(!t)return this.records;if("string"==typeof t||"number"==typeof t)return this.records.filter(e=>e.id===t);let e=Object.keys(t);return this.records.filter(r=>-1!==e.indexOf("id")?r.id===t.id:e.every(e=>r[e]===t[e]))}remove(t){if(!t||Array.isArray(t))return this._log(console.error("remove() accepts a single object")),[];let e=this.find(t);return e.forEach(t=>{this.records.splice(this.records.indexOf(t),1)}),this._save(),e}clear(){this.records=[],this._save()}_save(){this.store&&localStorage&&localStorage.setItem(this.store,JSON.stringify(this.records))}_load(){if(this.store&&localStorage)return JSON.parse(localStorage.getItem(this.store))||[]}dump(){!function(t,e){let r=document.createElement("a"),s=new Blob([e],{type:"text/plain"});r.href=URL.createObjectURL(s),r.download=t,r.click()}(`${this.store||"data"}.json`,JSON.stringify(this._load(),null,4))}}});
|
//# sourceMappingURL=record.min.js.map
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,10 @@
|
||||||
const gulp = require("gulp");
|
const gulp = require("gulp");
|
||||||
const minify = require('gulp-minify');
|
const minify = require("gulp-minify");
|
||||||
const strip = require("gulp-strip-comments");
|
const strip = require("gulp-strip-comments");
|
||||||
|
|
||||||
gulp.task("default", function() {
|
gulp.task("default", function() {
|
||||||
gulp.src("./src/record.js")
|
return gulp.src("./src/record.js")
|
||||||
.pipe(strip({safe: true}))
|
.pipe(strip({safe: true}))
|
||||||
.pipe(minify({ext: {min: ".min.js"}}))
|
.pipe(minify({ext: {min: ".min.js"}}))
|
||||||
.pipe(gulp.dest("dist"))
|
.pipe(gulp.dest("dist"));
|
||||||
});
|
});
|
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "record.js",
|
"name": "record.js",
|
||||||
"version": "0.6.1",
|
"version": "0.8.1",
|
||||||
"description": "dead-simple storage-collection library",
|
"description": "dead-simple storage-collection library",
|
||||||
"main": "src/record.js",
|
"main": "src/record.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
@ -9,8 +9,12 @@
|
||||||
"test": "test"
|
"test": "test"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node_modules/.bin/tape test/*.spec.js",
|
"build": "npm run build:es && npm run build:es:min && npm run build:cjs",
|
||||||
"build": "node_modules/.bin/gulp"
|
"build:es": "esbuild src/record.js --minify-whitespace --format=esm --outfile=dist/record.js",
|
||||||
|
"build:es:min": "esbuild src/record.js --minify --sourcemap --format=esm --outfile=dist/record.min.js",
|
||||||
|
"build:cjs": "esbuild src/record.js --minify-whitespace --format=cjs --platform=node --outfile=dist/record.cjs.js",
|
||||||
|
"test": "npm run lint && npm run build && npx tape test/*.spec.js",
|
||||||
|
"lint": "npx eslint src/record.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -26,11 +30,9 @@
|
||||||
],
|
],
|
||||||
"author": "Geoff Doty",
|
"author": "Geoff Doty",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gulp": "^3.9.1",
|
"eslint": "^8.21.0",
|
||||||
"gulp-minify": "^2.1.0",
|
"tape": "^5.5.3"
|
||||||
"gulp-strip-comments": "^2.5.2",
|
|
||||||
"jshint": "^2.9.5",
|
|
||||||
"tape": "^4.9.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
476
src/record.js
476
src/record.js
|
@ -1,103 +1,73 @@
|
||||||
/*! Record.js v0.6.1 | MIT | https://github.com/n2geoff/record.js */
|
/*! Record.js v0.8.0 | MIT | https://github.com/n2geoff/record.js */
|
||||||
(function (root, factory) {
|
/*
|
||||||
"use strict";
|
* Record.js
|
||||||
if (typeof module === "object" && module.exports) {
|
*
|
||||||
module.exports = factory(root.Record);
|
* A dead-simple object collection library
|
||||||
} else {
|
*/
|
||||||
root.Record = factory(root.Record);
|
class Record {
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // create a new record (in-memory)
|
||||||
|
* let pets = new Record();
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // create a new record (localStorage)
|
||||||
|
* let pets = new Record({"store": "pets"});
|
||||||
|
*
|
||||||
|
* @param {object} opts
|
||||||
|
* @param {object} opts.store - localStorage ID to use
|
||||||
|
* @param {object} opts.debug - show console logs
|
||||||
|
*/
|
||||||
|
constructor(opts) {
|
||||||
|
// define options
|
||||||
|
this.store = (opts || {}).store;
|
||||||
|
this.debug = (opts || {}).debug || false;
|
||||||
|
|
||||||
|
// initialize the collection
|
||||||
|
this.records = [];
|
||||||
|
|
||||||
|
// get stored records, merge as needed
|
||||||
|
if (this.store && localStorage) {
|
||||||
|
this._log("Initializing localStorage for " + this.store);
|
||||||
|
|
||||||
|
let existing = this._load() || [];
|
||||||
|
this.records = [...existing];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}(this, function () {
|
/**
|
||||||
"use strict";
|
* Supresses logs based on this.debug value
|
||||||
/*
|
*
|
||||||
* Record.js
|
* @private
|
||||||
*
|
*/
|
||||||
* A dead-simple object collection library
|
_log() {
|
||||||
*/
|
if (this.debug) {
|
||||||
class Record {
|
console.log(...arguments);
|
||||||
/**
|
|
||||||
* @constructor
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // create a new record (in-memory)
|
|
||||||
* let pets = new Record();
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* // create a new record (localStorage)
|
|
||||||
* let pets = new Record({"store": "pets"});
|
|
||||||
*
|
|
||||||
* @param {object} opts
|
|
||||||
* @param {object} opts.store - localStorage ID to use
|
|
||||||
* @param {object} opts.debug - show console logs
|
|
||||||
*/
|
|
||||||
constructor(opts) {
|
|
||||||
// define options
|
|
||||||
this.store = (opts || {}).store;
|
|
||||||
this.debug = (opts || {}).debug || false;
|
|
||||||
|
|
||||||
// initialize the collection
|
|
||||||
this.records = [];
|
|
||||||
|
|
||||||
// get stored records, merge as needed
|
|
||||||
if(this.store && localStorage) {
|
|
||||||
this._log("Initializing localStorage for " + this.store);
|
|
||||||
|
|
||||||
let existing = this._load() || [];
|
|
||||||
this.records = [...existing];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Supresses logs based on this.debug value
|
* Add record to collection creating an sudo unique id if
|
||||||
*
|
* one not provided
|
||||||
* @private
|
*
|
||||||
*/
|
* @example
|
||||||
_log() {
|
* // add pet to collection
|
||||||
if(this.debug) {
|
* let dog = pets.add({"name": "Yonkers", "age": 5});
|
||||||
console.log(...arguments);
|
* // > [{"id": "14rj345k9", "name": "Yonkers", "age": 5}]
|
||||||
}
|
*
|
||||||
}
|
* @param {Object} entry - object(s) you want to store
|
||||||
|
* @returns {object} entry added
|
||||||
|
*/
|
||||||
|
add(entry) {
|
||||||
|
|
||||||
/**
|
// for array of entries
|
||||||
* Add record to collection creating an sudo unique id if
|
if (Array.isArray(entry)) {
|
||||||
* one not provided
|
// temp array
|
||||||
*
|
let entries = [];
|
||||||
* @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) {
|
|
||||||
|
|
||||||
// for array of entries
|
entry.forEach(() => {
|
||||||
if (Array.isArray(entry)) {
|
|
||||||
// temp array
|
|
||||||
let entries = [];
|
|
||||||
|
|
||||||
entry.forEach(() => {
|
|
||||||
// add id if not provided
|
|
||||||
if (!entry.id) {
|
|
||||||
entry.id = Math.random().toString(36).substr(2, 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add entry to records collection
|
|
||||||
this.records.push(entry);
|
|
||||||
|
|
||||||
// used to return all added
|
|
||||||
this.entries.push(entry);
|
|
||||||
});
|
|
||||||
|
|
||||||
// save to storage
|
|
||||||
this._save();
|
|
||||||
|
|
||||||
// return added entry(s)
|
|
||||||
return entries;
|
|
||||||
|
|
||||||
// if single entry
|
|
||||||
} else {
|
|
||||||
// add id if not provided
|
// add id if not provided
|
||||||
if (!entry.id) {
|
if (!entry.id) {
|
||||||
entry.id = Math.random().toString(36).substr(2, 9);
|
entry.id = Math.random().toString(36).substr(2, 9);
|
||||||
|
@ -106,153 +76,199 @@
|
||||||
// add entry to records collection
|
// add entry to records collection
|
||||||
this.records.push(entry);
|
this.records.push(entry);
|
||||||
|
|
||||||
// save to storage
|
// used to return all added
|
||||||
this._save();
|
this.entries.push(entry);
|
||||||
|
|
||||||
// return added 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((val) => {
|
|
||||||
return record[val] === key[val];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// save to storage
|
// save to storage
|
||||||
this._save();
|
this._save();
|
||||||
|
|
||||||
// return records removed
|
// return added entry(s)
|
||||||
return entries;
|
return entries;
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
// if single entry
|
||||||
// erase records to empty array
|
} else {
|
||||||
this.records = [];
|
// add id if not provided
|
||||||
|
if (!entry.id) {
|
||||||
|
entry.id = Math.random().toString(36).substr(2, 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add entry to records collection
|
||||||
|
this.records.push(entry);
|
||||||
|
|
||||||
// save to storage
|
// save to storage
|
||||||
this._save();
|
this._save();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// return added entry
|
||||||
* save a record to storage if available
|
return entry;
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*
|
|
||||||
* @memberof Record
|
|
||||||
*/
|
|
||||||
_save() {
|
|
||||||
if (this.store && localStorage) {
|
|
||||||
localStorage.setItem(this.store, JSON.stringify(this.records));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* load records from storage if exists
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*
|
|
||||||
* @returns {array} of loaded records
|
|
||||||
* @memberof Record
|
|
||||||
*/
|
|
||||||
_load() {
|
|
||||||
if (this.store && localStorage) {
|
|
||||||
return JSON.parse(localStorage.getItem(this.store)) || [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps data to JSON file
|
|
||||||
*
|
|
||||||
* Uses 'store' as file name with a '.json' extension
|
|
||||||
*
|
|
||||||
* @return {object} JSON Object of records
|
|
||||||
*/
|
|
||||||
dump() {
|
|
||||||
function download(filename, content) {
|
|
||||||
let a = document.createElement("a");
|
|
||||||
let file = new Blob([content], {type: "text/plain"});
|
|
||||||
a.href = URL.createObjectURL(file);
|
|
||||||
a.download = filename;
|
|
||||||
a.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
download(`${this.store || "data"}.json`, JSON.stringify(this._load(), null, 4));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Record;
|
/**
|
||||||
}));
|
* Updates a Record
|
||||||
|
*
|
||||||
|
* @param {Object} entry
|
||||||
|
*/
|
||||||
|
update(entry) {
|
||||||
|
|
||||||
|
// find entry index
|
||||||
|
var idx = this.records.indexOf(entry);
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (this.records[idx].id === entry.id) {
|
||||||
|
|
||||||
|
// update record
|
||||||
|
this.records.splice(idx, 1, entry);
|
||||||
|
|
||||||
|
// save to storage
|
||||||
|
this._save();
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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((val) => {
|
||||||
|
return record[val] === key[val];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
// save to storage
|
||||||
|
this._save();
|
||||||
|
|
||||||
|
// return records removed
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
// erase records to empty array
|
||||||
|
this.records = [];
|
||||||
|
|
||||||
|
// save to storage
|
||||||
|
this._save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* save a record to storage if available
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @memberof Record
|
||||||
|
*/
|
||||||
|
_save() {
|
||||||
|
if (this.store && localStorage) {
|
||||||
|
localStorage.setItem(this.store, JSON.stringify(this.records));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* load records from storage if exists
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* @returns {array} of loaded records
|
||||||
|
* @memberof Record
|
||||||
|
*/
|
||||||
|
_load() {
|
||||||
|
if (this.store && localStorage) {
|
||||||
|
return JSON.parse(localStorage.getItem(this.store)) || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps data to JSON file
|
||||||
|
*
|
||||||
|
* Uses 'store' as file name with a '.json' extension
|
||||||
|
*
|
||||||
|
* @return {object} JSON Object of records
|
||||||
|
*/
|
||||||
|
dump() {
|
||||||
|
function download(filename, content) {
|
||||||
|
let a = document.createElement("a");
|
||||||
|
let file = new Blob([content], { type: "text/plain" });
|
||||||
|
a.href = URL.createObjectURL(file);
|
||||||
|
a.download = filename;
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
download(`${this.store || "data"}.json`, JSON.stringify(this._load(), null, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Record;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const test = require("tape");
|
const test = require("tape");
|
||||||
const Record = require("../src/record.js");
|
const Record = require("../dist/record.cjs.js").default;
|
||||||
|
|
||||||
test("Record.js", function(t) {
|
test("Record.js", function(t) {
|
||||||
let pets;
|
let pets;
|
||||||
|
@ -11,18 +11,18 @@ test("Record.js", function(t) {
|
||||||
t.ok(pets, "new collection should have been created");
|
t.ok(pets, "new collection should have been created");
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
t.test("should add 3 records one-by-one", function(t) {
|
t.test("should add 3 records one-by-one", function(t) {
|
||||||
t.ok(pets.add, "add method exists");
|
t.ok(pets.add, "add method exists");
|
||||||
|
|
||||||
let plato = pets.add({"name": "plato", "type": "dog"});
|
let plato = pets.add({"name": "plato", "type": "dog"});
|
||||||
let socrates = pets.add({"id": "1", "name": "socrates", "type": "dog"});
|
let socrates = pets.add({"id": "1", "name": "socrates", "type": "dog"});
|
||||||
let hypatia = pets.add({"name": "hypatia", "type": "cat"});
|
let hypatia = pets.add({"name": "hypatia", "type": "cat"});
|
||||||
|
|
||||||
t.equal(plato.name, "plato", "plato should have been added");
|
t.equal(plato.name, "plato", "plato should have been added");
|
||||||
t.equal(socrates.name, "socrates", "socrates 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.equal(hypatia.name, "hypatia", "hypatia should have been added");
|
||||||
|
|
||||||
t.ok(plato.id, "platos record id should be auto-generated");
|
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.equal(socrates.id, "1","socrates record id should not be auto-generated");
|
||||||
|
|
||||||
|
@ -34,6 +34,30 @@ test("Record.js", function(t) {
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test("should be able to update a record", function(t) {
|
||||||
|
|
||||||
|
let plato = pets.find({"name": "plato"})[0];
|
||||||
|
|
||||||
|
t.equal(plato.age, undefined, "should not have age yet");
|
||||||
|
|
||||||
|
plato.age = 80;
|
||||||
|
|
||||||
|
let update = pets.update(plato);
|
||||||
|
|
||||||
|
t.equal(update.age, 80, "should exist in db now");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test("should be able find pet", function(t) {
|
||||||
|
|
||||||
|
let plato = pets.find({"age": 80})[0];
|
||||||
|
|
||||||
|
t.ok(plato, "found plato");
|
||||||
|
|
||||||
|
t.equal(plato.name, "plato", "yep, that him");
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test("should find all matching records", function(t) {
|
t.test("should find all matching records", function(t) {
|
||||||
let dogs = pets.find({"type": "dog"});
|
let dogs = pets.find({"type": "dog"});
|
||||||
|
|
||||||
|
@ -49,7 +73,7 @@ test("Record.js", function(t) {
|
||||||
t.equal(hypatia[0].name, "hypatia", "hypatia should be removed");
|
t.equal(hypatia[0].name, "hypatia", "hypatia should be removed");
|
||||||
|
|
||||||
t.equal(pets.find().length, 2, "yes, hypatia has left the building");
|
t.equal(pets.find().length, 2, "yes, hypatia has left the building");
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue