Skip to content

Instantly share code, notes, and snippets.

@rudylattae
Created November 24, 2012 03:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rudylattae/4138220 to your computer and use it in GitHub Desktop.
Save rudylattae/4138220 to your computer and use it in GitHub Desktop.
Bunch.js
var Bunch = (function () {
// internals
var defaults = {
IDENTITY_FIELD: 'id',
SEQUENCE_START: 0,
hasIdentity: function hasIdentity( item ) {
return item[ this._identityField ];
},
ensureIdentity: function ensureIdentity( item ) {
var uniqueItem = clone( item );
if ( !this._hasIdentity( uniqueItem ) ) {
uniqueItem[ this._identityField ] = this._nextId();
}
return uniqueItem;
},
nextId: function nextId() {
return this._sequence = this._sequence - 1;
},
toModel: function toModel( item ) {
return item;
},
isTransient: function isTransient( model ) {
var id = model[ this._identityField ];
if ( typeof id === 'function' ) {
id = model.id();
}
return id < 0;
},
markDeleted: function markDeleted( model ) {
model._deleted = true;
},
isDeleted: function isDeleted( model ) {
return model._deleted === true;
}
};
// public api
function Bunch( options, data ) {
this._items = {};
this._hasIdentity = options.hasIdentity || defaults.hasIdentity;
this._ensureIdentity = options.ensureIdentity || defaults.ensureIdentity;
this._nextId = options.nextId || defaults.nextId;
this._toModel = options.toModel || defaults.toModel;
this._isTransient = options.isTransient || defaults.isTransient;
this._markDeleted = options.markDeleted || defaults.markDeleted;
this._isDeleted = options.isDeleted || defaults.isDeleted;
this._sequence = options.sequenceStart || defaults.SEQUENCE_START;
this._identityField = options.identityField || defaults.IDENTITY_FIELD;
if ( data ) {
data.forEach( function( item ) {
this.add( item, { silent: true });
}.bind( this ));
}
}
Bunch.prototype.add = function( item, options ) {
var options = options || { silent: false },
id,
uniqueItem,
model;
uniqueItem = this._ensureIdentity( item );
id = this._hasIdentity( uniqueItem );
if ( !this.has( id ) ) {
model = this._toModel( uniqueItem );
this._items[ id ] = model;
if ( !options.silent ) {
this.trigger( 'added', model );
}
}
};
Bunch.prototype.all = function() {
return this.toArray();
};
Bunch.prototype.get = function( id ) {
return this._items[ id ];
};
Bunch.prototype.map = function( callback, ctx ) {
var result = [],
i;
for ( i in this._items ) {
if( this._items.hasOwnProperty( i ) && !this._isDeleted( this._items[i] ) ) {
if ( ctx ) {
result.push( callback.call( ctx, this._items[i] ) );
} else {
result.push( callback( this._items[i] ) );
}
}
}
return result;
};
Bunch.prototype.remove = function( id, options ) {
var options = options || { destroy: false };
if ( this.has( id ) ) {
if ( options.destroy || this._isTransient(this._items[ id ]) ) {
this._destroy( id );
} else {
this._softDelete( id );
}
}
};
Bunch.prototype.has = function( id ) {
return this._items[ id ];
};
Bunch.prototype.toArray = function() {
return this.map(function( o ) {
return o;
}.bind( this ));
};
Bunch.prototype._destroy = function( id ) {
this.trigger( 'deleted', this._items[ id ] );
this._items[ id ] = null;
delete this._items[ id ];
};
Bunch.prototype._softDelete = function( id ) {
this.trigger( 'deleted', this._items[ id ] );
this._markDeleted( this._items[ id ] );
};
LucidJS.emitter( Bunch.prototype );
// utils
function clone( obj ) {
return JSON.parse( JSON.stringify(obj) );
}
return Bunch;
}());
function ScopedStore( store, scope ) {
function scoped( key ) {
return scope + '.' + key;
}
this.set = function(key, value) {
return store.set( scoped(key), value );
};
this.get = function(key) {
return store.get( scoped(key) );
};
this.remove = function(key) {
store.remove( scoped(key) );
};
this.clear = function() {
store.clear();
};
}
var LocalStoragePersistenceProvider = (function() {
function LocalStoragePersistenceProvider( store, collection ) {
}
LocalStoragePersistenceProvider.prototype.insert = function() {
};
LocalStoragePersistenceProvider.prototype.find = function() {
};
LocalStoragePersistenceProvider.prototype.update = function() {
};
LocalStoragePersistenceProvider.prototype.remove = function() {
};
return LocalStoragePersistenceProvider;
}());
/*
connect()
find()
insert()
update()
remove()
*/
function LocaAdapter( store, collection ) {
var data = [],
index = {},
nextId;
this.create = function( record ) {
record.id = nextId;
data.push( record );
index[ nextId ] = data.indexOf( record );
nextId = nextId + 1;
this.flush();
};
this.read = function( id ) {
return data[ index[id] ];
};
this.update = function( id, changes ) {
var record = data[ index[id] ];
for( p in changes ) {
if ( record.hasOwnProperty(p) ) {
record[p] = changes[p];
}
}
this.flush();
};
this.load = function( refresh ) {
if ( data && !refresh ) {
return data;
}
data = store.get( collection ) || [];
index = store.get( collection + '_index' ) || {};
nextId = store.get( collection + '_nextId' ) || 1;
return data;
};
this.nuke = function( confirm ) {
if ( confirm ) {
data = [];
index = {};
nextId = 1;
this.flush();
}
}
this.flush = function() {
store.set( collection, data);
store.set( collection + '_index', index);
store.set( collection + '_nextId', nextId);
}
}
function koRepositoryBoundObservableArray( repo ) {
var a = ko.observableArray( repo.all() );
repo.on('added', function( item ) {
a.push( item );
});
repo.on('deleted', function( item ) {
a.remove( item );
});
return a;
}
@rudylattae
Copy link
Author

    sstore = new ScopedStore( store, 'BlankStageDesktop');


    links = new xti.ormish.Collection({
        loader: function() {
            return sql.select().from('links').execute();
        },
        mapper: {
            model: function( item ) {
                if ( item instanceof Link ) return item;
                return new Link( item );
            },
            list: function( items ) {
                var a = ko.observableArray(),
                    i = 0,
                    max = items.length;
                for ( ; i < max; i ++ ) {
                    a.push( new Link(items[ i ]) );
                }
                return a;
            } 
        },
        crud: {
            add: function( item ) {
                var id = sql.insert( item ).into( 'links' ).execute();
                item.id( id );
                return item; 
            }
        }
    });
    links.on('added', function( item ) {
        this._sql.insert( item.toJS() ).into( this._table ).execute();
        this.update( {id: } )
    }.bind( this ));

    sync = new xti.ormish.SyncToDb();
    links.on('added', function( item ) {
        this._sql.insert( item.toJS() ).into( this._table ).execute();
        this.update( {id: } )
    }.bind( this ));

@rudylattae
Copy link
Author

function Collection( options ) {
    this._loader = options.loader || throw new Error('Provide a loader to fetch data');
    this._mapper = options.mapper;
    this._data = [];
}
Collection.prototype.all = function() {
    return this._data;
};
Collection.prototype.add = function( item ) {
    var model = null;
    if ( this._crud.add ) {
        model = this._mapper.model( this._crud.add( item ) );
    }
    this._data.push( model );
    this.trigger( 'added', model );
};
Collection.prototype.find = function( condition, fields ) {
    return this._sql.select( fields ).from( this._table ).where( condition ).execute();
};
Collection.prototype.update = function( condition, changes ) {
    return this._sql.update( this._table ).set( changes ).where( condition ).execute();
};
Collection.prototype.remove = function( condition ) {
    return this._sql.remove().from( this._table ).where( condition ).execute();
};
Collection.prototype.load = function( refresh ) {
    if ( this._data && !refresh ) {
        return;
    }

    var raw = this._loader();
    if ( this._mapper )
        this._data = this._mapper.list( raw );
    else 
        this._data = raw;

    this.trigger( 'refreshed' );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment