diff --git a/examples/ini.js b/examples/ini.js index 893d9b6..1f3f695 100644 --- a/examples/ini.js +++ b/examples/ini.js @@ -12,14 +12,14 @@ function writeFile() { } // Will be called in context of store -function readParseFile() { +function readFile() { let data; - - console.log('readParseFile'); + + console.log('readFile'); try { data = fs.readFileSync(this.path, 'utf-8'); } catch (e) { - console.log('readParseFile error; starting with empty data'); + console.log('readFile error; starting with empty data'); data = {}; } @@ -30,8 +30,8 @@ function readParseFile() { const store = new Store('app', { path: __dirname + '/data.ini', debounce: 10, - writeFile: writeFile, - readParseFile: readParseFile + writeFile, + readFile }); store.merge('section 1', { a : 'b' }); diff --git a/index.js b/index.js index 205adb9..7c07dda 100644 --- a/index.js +++ b/index.js @@ -53,8 +53,8 @@ class Store { this.timeouts = {}; // Allow override of read and write methods - if (typeof options.readParseFile === 'function') { - this.readParseFile = options.readParseFile; + if (typeof options.readFile === 'function') { + this.readFile = options.readFile; } if (typeof options.writeFile === 'function') { this.writeFile = options.writeFile; @@ -77,11 +77,12 @@ class Store { * @name .set * @param {String} `key` * @param {any} `val` The value to save to `key`. Must be a valid JSON type: String, Number, Array or Object. + * @param {Boolean|Function} `merge` Option to use `Object.assign` or specified function to merge value onto the property. Passed to [set-value][]. * @return {Object} `Store` for chaining * @api public */ - set(key, val) { + set(key, val, merge) { if (typeof key === 'string' && typeof val === 'undefined') { return this.del(key); } @@ -92,46 +93,13 @@ class Store { } } else { assert.equal(typeof key, 'string', 'expected key to be a string'); - set(this.data, key, val); + set(this.data, key, val, { merge }); } this.save(); return this; } - /** - * Assign `value` to `key` while retaining prior members of `value` if - * `value` is a map. If `value` is not a map, overwrites like `.set`. - * - * ```js - * store.set('a', { b: c }); - * //=> {a: { b: c }} - * - * store.merge('a', { d: e }); - * //=> {a: { b: c, d: e }} - * - * store.set('a', 'b'); - * //=> {a: 'b'} - * - * store.merge('a', { c : 'd' }); - * //=> {a: { c : 'd' }} - * ``` - * - * @name .merge - * @param {String} `key` - * @param {any} `val` The value to merge to `key`. Must be a valid JSON type: String, Number, Array or Object. - * @return {Object} `Store` for chaining - * @api public - */ - - merge(key, val) { - let oldVal = this.get(key); - if (oldVal && typeof oldVal === 'object' && !Array.isArray(oldVal)) { - val = Object.assign(this.get(key), val); - } - return this.set(key, val); - } - /** * Add the given `value` to the array at `key`. Creates a new array if one * doesn't exist, and only adds unique values to the array. @@ -373,18 +341,17 @@ class Store { } /** - * Read and parse the data from the file system store. This method should - * probably not be called directly. Unless you are familiar with the inner - * workings of the code it's recommended that you use .load() instead. + * Read file at `this.path` and return the parsed data using the specified + * `readFile` option. Defaults to reading and parsing JSON data. * * ```js - * data = store.readPraseFile(); + * data = store.readFile(); * ``` - * @name .readParseFile + * @name .readFile * @return {Object} */ - readParseFile() { + readFile() { return JSON.parse(fs.readFileSync(this.path)); } @@ -395,7 +362,7 @@ class Store { load() { try { - return (this[kData] = this.readParseFile()); + return (this[kData] = this.readFile()); } catch (err) { if (err.code === 'EACCES') { err.message += '\ndata-store does not have permission to load this file\n'; diff --git a/package.json b/package.json index 305fd0d..4071093 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "delete": "^1.1.0", "gulp-format-md": "^2.0.0", "ini": "^1.3.5", + "merge-deep": "^3.0.2", "mocha": "^6.2.0" }, "keywords": [ diff --git a/test/test.js b/test/test.js index dd05287..86e77ef 100644 --- a/test/test.js +++ b/test/test.js @@ -5,6 +5,7 @@ const fs = require('fs'); const path = require('path'); const del = require('delete'); const assert = require('assert'); +const merge = require('merge-deep'); const dataStore = require('..'); const tests = (...args) => path.resolve(__dirname, ...args); const storePath = tests('fixtures/tests.json'); @@ -103,19 +104,19 @@ describe('store', () => { describe('merge', () => { it('should allow adding to an existing map', () => { - store.merge('a', { b : 'c' }); - store.merge('d', { e : 'f' }); + store.set('a', { b : 'c' }, true); + store.set('a', { e : 'f' }, true); store.set('g', { h : 'i' }); assert.equal(store.data.a.b, 'c'); - assert.equal(store.data.d.e, 'f'); + assert.equal(store.data.a.e, 'f'); assert.equal(store.data.g.h, 'i'); }); it('should allow overriding an existing key in an existing map', () => { - store.merge('a', { b : 'c' }); - store.merge('d', { e : 'f' }); + store.set('a', { b : 'c' }, true); + store.set('d', { e : 'f' }, true); store.set('g', { h : 'i' }); - store.merge('d', { e : 'j' }); + store.set('d', { e : 'j' }, true); assert.equal(store.data.a.b, 'c'); assert.equal(store.data.d.e, 'j'); assert.equal(store.data.g.h, 'i'); @@ -123,9 +124,21 @@ describe('store', () => { it('should just overwrite if merge to non-object', () => { store.set('a', 'b'); - store.merge('a', { c : 'd' }); + store.set('a', { c : 'd' }, true); assert.equal(store.data.a.c, 'd'); }); + + it('should use a custom merge function', () => { + store.set('a', { b: 'c', d: { e: 'f' } }); + store.set('a', { g: 'h', d: { i: 'j' } }, merge); + assert.deepEqual(store.data, { + a: { + b: 'c', + d: { e: 'f', i: 'j' }, + g: 'h' + } + }); + }); }); describe('union', () => {