Skip to content

Instantly share code, notes, and snippets.

@ghinch
Created October 7, 2010 18:11
Show Gist options
  • Save ghinch/615571 to your computer and use it in GitHub Desktop.
Save ghinch/615571 to your computer and use it in GitHub Desktop.
var Book = Y.Base.create('book', Y.Model, [], {}, {
PRIMARY_KEY : 'id',
ATTRS : {
author : {},
id : {},
title : {},
published : {},
format : {}
}
});
var BookSource = new Y.DataSource.Local({
source : '/fetch/books/',
plugins : [
{fn : Y.Plugin.DataSourceArraySchema, cfg : {schema : {}}},
{fn : Y.Plugin.DataSourceModelMapper, cfg : {
model : Book,
map : {
id : 'id',
author_id : 'author',
book_title : 'title',
published : 'published'
}
}}
]
});
YUI.add('model-mapper', function (Y) {
Y.namespace('Plugin').DataSourceModelMapper = Y.Base.create('model-mapper', Y.Plugin.Base, [], {
_mapData : function (data) {
var map = this.get('map'),
mappedData = (map ? {} : data);
Y.Object.each(map, function (val, key, obj) {
if (key in data) {
mappedData[val] = data[key];
}
});
return mappedData;
},
_resultsToModels : function (results) {
var resultSet = [],
model = this.get('model');
Y.Array.each(results, Y.rbind(function (result, index, arr) {
var data = this._mapData(result),
m = (model ? new model(data) : data);
if (m) {
resultSet.push(m);
}
}, this));
return resultSet;
},
_beforeDefResponseFn : function (e) {
var results = e.response.results;
if (results) {
e.response.results = this._resultsToModels(results);
} else {
e.response.results = [];
}
Y.DataSource.Local.issueCallback(e, this);
return new Y.Do.Halt("ModelManager plugin halted defResponseFn");
},
initializer : function () {
this.doBefore('_defResponseFn', this._beforeDefResponseFn);
}
}, {
NS : 'mapper',
ATTRS : {
map : {},
model : {}
}
});
}, '', {requires : ['collection', 'base-build', 'datasource-local', 'plugin']});
YUI.add('model', function (Y) {
Y.Model = Y.Base.create('model', Y.Base, [], {
_id : null,
// These methods allow setters to be strings for primitive types
'string' : function (val, key) {
return String(val);
},
'number' : function (val, key) {
return Number(val);
},
'date' : function (val, key) {
return new Date(val);
},
initializer : function (cfg) {
this.once('initializedChange', function (e) {
var c = this.constructor,
id = this.get('_id');
if (!c._instances) {
c._instances = {};
}
c._instances[id] = this;
}, this);
},
destructor : function () {
var c = this.constructor,
id = this.get('_id');
delete c._instances[id];
},
// Compare if two instances are the same by turning them into objects and comparing them as JSON.
// Would be nice if we had md5 here... this may have problems though, if Model references are included,
// may just need to do top level comparison
compare : function (other) {
if (!other instanceof this.constructor) {
return false;
}
var selfHash = Y.JSON.stringify(this.toObject()),
otherHash = Y.JSON.stringify(other.toObject());
return selfHash == otherHash;
},
// Turns this model instance into a Javascript object which can be serialized into another data format
toObject : function () {
var c = this.constructor,
members = c.members(),
obj = {};
Y.Array.each(members, function (member) {
obj[member] = this.get(member);
}, this);
return obj;
}
}, {
NS : 'model',
ATTRS : {
primaryKey : {
readOnly : true,
valueFn : function () {
return this.constructor.PRIMARY_KEY;
}
},
'_id' : {
readOnly : true,
getter : function () {
var pk = this.get('primaryKey'),
id = (pk ? this.get(pk) : null);
if (!id) {
if (!this._id) {
this._id = Y.guid();
}
id = this._id;
}
return id;
}
}
},
getById : function (id) {
return this._instances[id];
},
// Class method for returning a list of all the member properties (ATTRS) of this constructor,
// up the inheritance chain
members : function () {
var c = this.prototype.constructor,
map = [];
while (c) {
try {
if (c.ATTRS) {
Y.Object.each(c.ATTRS, function (val, key) {
if (!val.readOnly) {
map.push(key);
}
});
}
} catch (err) {}
c = (c.superclass ? c.superclass.constructor : null);
}
return map;
},
// Utility method to create a reference to another Model constructor as the
// value to set for a property.
// @TODO : Look into making this lazy to a remote source
reference : function (model) {
if (model.prototype) {
function createModel (o, key, construct) {
try {
if (Y.Lang.isString(o) || Y.Lang.isNumber(o)) {
return construct.getById(o);
} else {
return new construct(cfg);
}
} catch (err) {}
}
return Y.rbind(createModel, this, model);
}
},
// Same as above but creates an array of Models rather than just one
referenceMany : function (model) {
if (model.prototype) {
function createModels (cfg, key, construct) {
var data = [];
try {
Y.Array.each(cfg, function (o) {
data.push((Y.Lang.isString(o) || Y.Lang.isNumber(o)) ? construct.getById(o) : new construct(o));
});
} finally {
return data;
}
}
return Y.rbind(createModels, this, model);
}
}
});
function isModel (o) {
var c = o.superclass.constructor;
while (c.superclass) {
if (c == Y.Model) {
return true
}
c = c.superclass.constructor;
}
return false;
}
// Propogates additional data needed for models
Y.mix(Y.Base._buildCfg.custom, {
getById : function (prop, r, s) {
if (isModel(r)) {
r.getById = Y.Model.getById;
}
},
members : function (prop, r, s) {
if (isModel(r)) {
r.members = Y.Model.members;
}
}
});
}, '', {requires : ['base-build', 'datatype-date', 'json-stringify']});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment