diff --git a/src/store/DiffStore.js b/src/store/DiffStore.js index a0d51b9..6bf6d95 100644 --- a/src/store/DiffStore.js +++ b/src/store/DiffStore.js @@ -132,6 +132,31 @@ module.exports = Class( 'DiffStore' ) }, + /** + * Populate store with each element in object `obj` + * + * This is simply a convenient way to call `#add` for each element in an + * object. This does directly call `#add`, so overriding that method + * will also affect this one. + * + * If the intent is to change the behavior of what happens when an item + * is added to the store, override the `#add` method instead of this one + * so that it affects _all_ adds, not just calls to this method. + * + * @param {Object} obj object with which to populate store + * + * @return {Array.>} array of #add promises + */ + 'virtual public populate'( obj ) + { + return Promise.all( + Object.keys( obj ).map( + key => this.add( key, obj[ key ] ) + ) + ); + }, + + /** * Retrieve diff of `key` * diff --git a/src/store/MemoryStore.js b/src/store/MemoryStore.js index e7a4dbc..e51fe20 100644 --- a/src/store/MemoryStore.js +++ b/src/store/MemoryStore.js @@ -89,6 +89,31 @@ module.exports = Class( 'MemoryStore' ) }, + /** + * Populate store with each element in object `obj` + * + * This is simply a convenient way to call `#add` for each element in an + * object. This does directly call `#add`, so overriding that method + * will also affect this one. + * + * If the intent is to change the behavior of what happens when an item + * is added to the store, override the `#add` method instead of this one + * so that it affects _all_ adds, not just calls to this method. + * + * @param {Object} obj object with which to populate store + * + * @return {Array.>} array of #add promises + */ + 'virtual public populate'( obj ) + { + return Promise.all( + Object.keys( obj ).map( + key => this.add( key, obj[ key ] ) + ) + ); + }, + + /** * Retrieve item from store under `key` * diff --git a/src/store/Store.js b/src/store/Store.js index 7913063..3ceca6b 100644 --- a/src/store/Store.js +++ b/src/store/Store.js @@ -1,7 +1,7 @@ /** * Generic key/value store * - * Copyright (C) 2016 R-T Specialty, LLC. + * Copyright (C) 2016, 2017 R-T Specialty, LLC. * * This file is part of the Liza Data Collection Framework * @@ -50,6 +50,24 @@ module.exports = Interface( 'Store', 'public add': [ 'key', 'value' ], + /** + * Populate store with each element in object `obj` + * + * This is simply a convenient way to call `#add` for each element in an + * object. This does directly call `#add`, so overriding that method + * will also affect this one. + * + * If the intent is to change the behavior of what happens when an item + * is added to the store, override the `#add` method instead of this one + * so that it affects _all_ adds, not just calls to this method. + * + * @param {Object} obj object with which to populate store + * + * @return {Array.>} array of #add promises + */ + 'public populate': [ 'obj' ], + + /** * Retrieve item from store under `key` * diff --git a/test/store/DiffStoreTest.js b/test/store/DiffStoreTest.js index 2807738..7088276 100644 --- a/test/store/DiffStoreTest.js +++ b/test/store/DiffStoreTest.js @@ -232,4 +232,39 @@ describe( 'store.DiffStore', () => } ); } ); } ); + + + describe( '#populate', () => + { + it( "#add's each element of object to store", () => + { + const obj = { foo: {}, bar: {} }; + const sut = Sut(); + + return sut.populate( obj ) + .then( ps => + { + // by reference + expect( sut.get( 'foo' ) ) + .to.eventually.equal( obj.foo ); + expect( sut.get( 'bar' ) ) + .to.eventually.equal( obj.bar ); + + expect( ps.length ) + .to.equal( Object.keys( obj ).length ); + } ); + } ); + + it( "fails if any add fails", () => + { + const e = Error( 'ok' ); + + const sut = Sut.extend( { + 'override add': ( k, v ) => Promise.reject( e ) + } )(); + + return expect( sut.populate( { a: 1 } ) ) + .to.eventually.be.rejectedWith( e ); + } ); + } ); } ); diff --git a/test/store/MemoryStoreTest.js b/test/store/MemoryStoreTest.js index 6b5141b..575104e 100644 --- a/test/store/MemoryStoreTest.js +++ b/test/store/MemoryStoreTest.js @@ -71,6 +71,41 @@ describe( 'store.MemoryStore', () => } ); + describe( '#populate', () => + { + it( "#add's each element of object to store", () => + { + const obj = { foo: {}, bar: {} }; + const sut = Sut(); + + return sut.populate( obj ) + .then( ps => + { + // by reference + expect( sut.get( 'foo' ) ) + .to.eventually.equal( obj.foo ); + expect( sut.get( 'bar' ) ) + .to.eventually.equal( obj.bar ); + + expect( ps.length ) + .to.equal( Object.keys( obj ).length ); + } ); + } ); + + it( "fails if any add fails", () => + { + const e = Error( 'ok' ); + + const sut = Sut.extend( { + 'override add': ( k, v ) => Promise.reject( e ) + } )(); + + return expect( sut.populate( { a: 1 } ) ) + .to.eventually.be.rejectedWith( e ); + } ); + } ); + + // most things implicitly tested above describe( '#get', () => { @@ -118,7 +153,7 @@ describe( 'store.MemoryStore', () => describe( 'with mixin', () => { - it( 'allows overriding add', done => + it( 'allows overriding #add', done => { const expected_key = 'foo'; const expected_value = {}; @@ -137,7 +172,28 @@ describe( 'store.MemoryStore', () => } ); - it( 'allows overriding get', done => + it( "allows overriding #populate", () => + { + const obj = {}; + let called = false; + + return Sut.use( + Trait.extend( Sut, + { + 'override populate'( given ) + { + expect( given ).to.equal( obj ); + called = true; + + return Promise.resolve( true ); + } + } ) + )().populate( obj ) + .then( () => expect( called ).to.equal( true ) ); + } ); + + + it( 'allows overriding #get', done => { const expected_key = 'bar'; @@ -154,7 +210,7 @@ describe( 'store.MemoryStore', () => } ); - it( 'allows overriding clear', done => + it( 'allows overriding #clear', done => { Sut.use( Trait.extend( Sut,