Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Channel no longer overrides bucket methods
A bucket instance is given a channel instance and it uses the appropriate methods to communicate with the channel
- Loading branch information
1 parent
8141af2
commit 1b732e1
Showing
3 changed files
with
174 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,60 +1,164 @@ | |||
import { EventEmitter } from 'events' | import { EventEmitter } from 'events' | ||
import { inherits } from 'util' | import { inherits } from 'util' | ||
import uuid from 'node-uuid'; | import { v4 as uuid } from 'uuid'; | ||
|
|
||
export default function Bucket( name, storeProvider ) { | const callbackAsPromise = ( callback, task ) => new Promise( ( resolve, reject ) => { | ||
task( ( error, result ) => { | |||
if ( error ) { | |||
reject( error ); | |||
return; | |||
} | |||
resolve( result ); | |||
} ); | |||
} ); | |||
|
|||
const deprecateCallback = ( callback, promise ) => { | |||
if ( typeof callback === 'function' ) { | |||
// TODO: warn about deprecating callback API | |||
// and convert to promises | |||
return promise.then( | |||
result => { | |||
callback( null, result ); | |||
return result; | |||
}, | |||
error => { | |||
callback( error ); | |||
return error; | |||
} | |||
); | |||
} | |||
return promise; | |||
}; | |||
|
|||
const promiseAPI = store => ( { | |||
get: ( id, callback ) => | |||
callbackAsPromise( callback, store.get.bind( store, id ) ), | |||
update: ( id, object, isIndexing, callback ) => | |||
callbackAsPromise( callback, store.update.bind( store, id, object, isIndexing ) ), | |||
remove: ( id, callback ) => | |||
callbackAsPromise( callback, store.remove.bind( store, id ) ), | |||
find: ( query, callback ) => | |||
callbackAsPromise( callback, store.find.bind( store, query ) ) | |||
} ); | |||
|
|||
export default function Bucket( name, storeProvider, channel ) { | |||
EventEmitter.call( this ); | EventEmitter.call( this ); | ||
this.name = name; | this.name = name; | ||
this.store = storeProvider( this ); | this.store = storeProvider( this ); | ||
this.storeAPI = promiseAPI( this.store ); | |||
this.isIndexing = false; | this.isIndexing = false; | ||
this.channel = channel; | |||
|
|||
channel | |||
// forward the index and error events from the channel | |||
.on( 'index', ( ... args ) => this.emit( 'index', ... args ) ) | |||
.on( 'error', ( ... args ) => this.emit( 'error', ... args ) ) | |||
// when the channel updates or removes data, the bucket should apply | |||
// the same updates | |||
.on( 'update', ( id, data ) => { | |||
this.update( id, data, { sync: false } ); | |||
} ) | |||
.on( 'indexingStateChange', ( isIndexing ) => { | |||
this.isIndexing = isIndexing; | |||
if ( isIndexing ) { | |||
this.emit( 'indexing' ); | |||
} | |||
} ) | |||
.on( 'remove', ( id ) => { | |||
// TODO, there needs te be a way to remove without telling the | |||
// channel to do it | |||
this.remove( id ); | |||
} ); | |||
} | } | ||
|
|
||
inherits( Bucket, EventEmitter ); | inherits( Bucket, EventEmitter ); | ||
|
|
||
Bucket.prototype.reload = function() { | Bucket.prototype.reload = function() { | ||
this.emit( 'reload' ); | this.channel.reload(); | ||
}; | }; | ||
|
|
||
/** | |||
* Stores an object in the bucket and syncs it to simperium. Generates an | |||
* object ID to represent the object in simperium. | |||
* | |||
* @param {Object} object - plain js object literal to be saved/synced | |||
* @param {Function} callback - runs when object has been saved | |||
* @return {Promise<Object>} data stored in the bucket | |||
*/ | |||
Bucket.prototype.add = function( object, callback ) { | Bucket.prototype.add = function( object, callback ) { | ||
var id = uuid.v4(); | var id = uuid(); | ||
return this.update( id, object, callback ); | return this.update( id, object, callback ); | ||
}; | }; | ||
|
|
||
/** | |||
* Requests the object data stored in the bucket for the given id. | |||
* | |||
* @param {String} id - bucket object id | |||
* @param {Function} callback - with the data stored in the bucket | |||
* @return {Promise<Object>} the object data for the given id | |||
*/ | |||
Bucket.prototype.get = function( id, callback ) { | Bucket.prototype.get = function( id, callback ) { | ||
return this.store.get( id, callback ); | return deprecateCallback( callback, this.storeAPI.get( id ) ); | ||
}; | }; | ||
|
|
||
/** | |||
* Update the bucket object of `id` with the given data. | |||
* | |||
* @param {String} id - the bucket id for the object to update | |||
* @param {Object} data - object literal to replace the object data with | |||
* @param {Object} [options] - optional settings | |||
* @param {Boolean} [options.sync=true] - false if object should not be synced with this update | |||
* @param {Function} callback - executed when object is updated localy | |||
* @returns {Promise<Object>} - update data | |||
*/ | |||
Bucket.prototype.update = function( id, data, options, callback ) { | Bucket.prototype.update = function( id, data, options, callback ) { | ||
if ( typeof options === 'function' ) { | if ( typeof options === 'function' ) { | ||
callback = options; | callback = options; | ||
options = { sync: true }; | |||
} | |||
|
|||
if ( !! options === false ) { | |||
options = { sync: true }; | |||
} | } | ||
return this.store.update( id, data, this.isIndexing, callback ); |
|
||
const task = this.storeAPI.update( id, data, this.isIndexing ) | |||
.then( bucketObject => { | |||
this.emit( 'update', id, bucketObject.data ); | |||
this.channel.update( bucketObject, options.sync ); | |||
return bucketObject; | |||
} ); | |||
return deprecateCallback( callback, task ); | |||
}; | }; | ||
|
|
||
Bucket.prototype.hasLocalChanges = function( callback ) { | Bucket.prototype.hasLocalChanges = function( callback ) { | ||
callback( null, false ); | return deprecateCallback( callback, this.channel.hasLocalChanges() ); | ||
}; | }; | ||
|
|
||
Bucket.prototype.getVersion = function( id, callback ) { | Bucket.prototype.getVersion = function( id, callback ) { | ||
callback( null, 0 ); | return deprecateCallback( callback, this.channel.getVersion( id ) ); | ||
}; | }; | ||
|
|
||
Bucket.prototype.touch = function( id, callback ) { | Bucket.prototype.touch = function( id, callback ) { | ||
return this.store.get( id, ( e, object ) => { | const task = this.storeAPI.get( id ) | ||
if ( e ) return callback( e ); | .then( object => this.update( object.id, object.data ) ); | ||
this.update( object.id, object.data, callback ); |
|
||
} ); | return deprecateCallback( callback, task ); | ||
}; | }; | ||
|
|
||
Bucket.prototype.remove = function( id, callback ) { | Bucket.prototype.remove = function( id, callback ) { | ||
return this.store.remove( id, callback ); | const task = this.storeAPI.remove( id ) | ||
.then( ( result ) => { | |||
this.emit( 'remove', id ); | |||
this.channel.remove( id ); | |||
return result; | |||
} ) | |||
return deprecateCallback( callback, task ); | |||
}; | }; | ||
|
|
||
Bucket.prototype.find = function( query, callback ) { | Bucket.prototype.find = function( query, callback ) { | ||
return this.store.find( query, callback ); | return deprecateCallback( callback, this.storeAPI.find( query ) ); | ||
}; | }; | ||
|
|
||
Bucket.prototype.getRevisions = function( id, callback ) { | Bucket.prototype.getRevisions = function( id, callback ) { | ||
// Overridden in Channel | return deprecateCallback( callback, this.channel.getRevisions( id ) ); | ||
callback( new Error( 'Failed to fetch revisions for' + id ) ); | |||
} | } |
Oops, something went wrong.