diff --git a/dist/js-model-0.6.1.js b/dist/js-model-0.6.1.js new file mode 100644 index 0000000..c29a7e3 --- /dev/null +++ b/dist/js-model-0.6.1.js @@ -0,0 +1,290 @@ +/* js-model JavaScript library, version 0.6.1 + * (c) 2010 Ben Pickles + * + * Released under MIT license. + */ +var Model = function(name, methods) { + var model = function(attributes) { + this._name = name; + this.attributes = attributes || {}; + this.changes = {}; + this.collection = collection; + this.errors = []; + this.trigger('initialize'); + }; + + var collection; + if (methods && methods.collection) { + collection = methods.collection; + delete methods.collection; + } else { + collection = Model.Collection(); + }; + + model = $.extend(model, collection); + + model.prototype = $.extend({ + attr: function(name, value) { + if (arguments.length == 2) { + if (_.isEqual(this.attributes[name], value)) { + delete this.changes[name]; + } else { + this.changes[name] = value; + }; + return this; + } else if (typeof name == "object") { + for (var key in name) { + this.attr(key, name[key]); + }; + return this; + } else { + return (name in this.changes) ? + this.changes[name] : + this.attributes[name]; + }; + }, + + callPersistMethod: function(method, callback) { + var self = this; + + var manageCollection = function() { + if (!self.collection) return; + if (method == "create") { + self.collection.add(self); + } else if (method == "destroy") { + self.collection.remove(self.id()); + }; + }; + + var wrappedCallback = function(success) { + if (success) { + manageCollection(); + + self.trigger(method); + }; + + var value; + + if (callback) value = callback.apply(self, arguments); + + return value; + }; + + if (this.persistence) { + this.persistence[method](this, wrappedCallback); + } else { + wrappedCallback.call(this, true); + }; + }, + + destroy: function(callback) { + this.callPersistMethod("destroy", callback); + return this; + }, + + id: function() { + return this.attributes.id || null; + }, + + merge: function(attributes) { + $.extend(this.attributes, attributes); + return this; + }, + + newRecord: function() { + return this.id() == null; + }, + + reset: function() { + this.changes = {}; + return this; + }, + + save: function(callback) { + if (!this.valid()) return false; + + this.merge(this.changes).reset(); + + var method = this.newRecord() ? "create" : "update"; + this.callPersistMethod(method, callback); + + return true; + }, + + toParam: function() { + var params = {}; + for (var attr in this.attributes) { + var value = this.attributes[attr]; + if (attr != 'id' && value != null) { + params[this._name + '[' + attr + ']'] = value; + }; + }; + return params; + }, + + trigger: function(name) { + $(document).trigger([name, this._name].join('.'), [this]); + return this; + }, + + update: function(attributes) { + this.merge(attributes).trigger("update"); + return this; + }, + + valid: function() { + this.errors = []; + this.validate(); + return this.errors.length == 0; + }, + + validate: function() { + return this; + } + }, methods); + + return model; +}; +Model.Collection = function(methods) { + var model_collection = function(collection) { + this.collection = collection || []; + }; + + var chain = function(collection) { + return new model_collection(collection); + }; + + model_collection.prototype = $.extend({ + add: function() { + for (var i = 0; i < arguments.length; i++) { + var model = arguments[i]; + var existing_elem = this.detect(function() { + return this.id() != null && this.id() == model.id(); + }); + + if (!existing_elem) { + this.collection.push(arguments[i]); + } + }; + return this; + }, + + all: function() { + return this.collection; + }, + + detect: function(func) { + return _.detect(this.collection, function(model, i) { + return func.call(model, i); + }) || null; + }, + + each: function(func) { + $.each(this.collection, function(i) { + func.call(this, i); + }); + return this; + }, + + find: function(id) { + return this.detect(function() { + return this.id() == id; + }); + }, + + first: function() { + return this.collection[0] || null; + }, + + last: function() { + return this.collection[this.collection.length - 1] || null; + }, + + remove: function(id) { + var ids = _.invoke(this.collection, 'id'); + var index = _.indexOf(ids, id); + if (index > -1) { + this.collection.splice(index, 1); + return true; + } else { + return false; + }; + }, + + select: function(func) { + var selected = _.select(this.collection, function(model, i) { + return func.call(model, i); + }); + return chain(selected); + }, + + sort: function(func) { + var sorted = _.sortBy(this.collection, function(model, i) { + return func.call(model, i); + }); + return chain(sorted); + } + }, methods); + + return new model_collection(); +}; +Model.RestPersistence = function(resource, methods) { + var model_resource = function() { + this.resource = resource; + }; + + model_resource.prototype = $.extend({ + create: function(model, callback) { + var wrappedCallback = function(success, data, xhr) { + this.merge(data); + + if (callback) callback.apply(this, arguments); + }; + + return this.xhr('POST', this.create_path(model), model, wrappedCallback); + }, + + create_path: function(model) { + return this.resource; + }, + + destroy: function(model, callback) { + return this.xhr('DELETE', this.destroy_path(model), null, callback); + }, + + destroy_path: function(model) { + return this.update_path(model); + }, + + update: function(model, callback) { + var wrappedCallback = function(success, data, xhr) { + this.merge(data); + + if (callback) callback.apply(this, arguments); + }; + + return this.xhr('PUT', this.update_path(model), model, wrappedCallback); + }, + + update_path: function(model) { + return [this.resource, model.id()].join('/'); + }, + + xhr: function(method, url, model, callback) { + return $.ajax({ + type: method, + url: url, + dataType: "json", + data: model ? model.toParam() : null, + failure: function(data, status, xhr) { + if (callback) callback.call(model, false, data, xhr); + }, + success: function(data, status, xhr) { + if (callback) callback.call(model, true, data, xhr); + } + }); + } + }, methods); + + return new model_resource(); +}; diff --git a/dist/js-model-0.6.1.min.js b/dist/js-model-0.6.1.min.js new file mode 100644 index 0000000..42310d8 --- /dev/null +++ b/dist/js-model-0.6.1.min.js @@ -0,0 +1,14 @@ +/* js-model JavaScript library, version 0.6.1 + * (c) 2010 Ben Pickles + * + * Released under MIT license. + */ +var Model=function(g,e){var f=function(a){this._name=g;this.attributes=a||{};this.changes={};this.collection=b;this.errors=[];this.trigger("initialize")},b;if(e&&e.collection){b=e.collection;delete e.collection}else b=Model.Collection();f=$.extend(f,b);f.prototype=$.extend({attr:function(a,d){if(arguments.length==2){if(_.isEqual(this.attributes[a],d))delete this.changes[a];else this.changes[a]=d;return this}else if(typeof a=="object"){for(var c in a)this.attr(c,a[c]);return this}else return a in this.changes? +this.changes[a]:this.attributes[a]},callPersistMethod:function(a,d){var c=this,h=function(){if(c.collection)if(a=="create")c.collection.add(c);else a=="destroy"&&c.collection.remove(c.id())},j=function(i){if(i){h();c.trigger(a)}var k;if(d)k=d.apply(c,arguments);return k};this.persistence?this.persistence[a](this,j):j.call(this,true)},destroy:function(a){this.callPersistMethod("destroy",a);return this},id:function(){return this.attributes.id||null},merge:function(a){$.extend(this.attributes,a);return this}, +newRecord:function(){return this.id()==null},reset:function(){this.changes={};return this},save:function(a){if(!this.valid())return false;this.merge(this.changes).reset();this.callPersistMethod(this.newRecord()?"create":"update",a);return true},toParam:function(){var a={};for(var d in this.attributes){var c=this.attributes[d];if(d!="id"&&c!=null)a[this._name+"["+d+"]"]=c}return a},trigger:function(a){$(document).trigger([a,this._name].join("."),[this]);return this},update:function(a){this.merge(a).trigger("update"); +return this},valid:function(){this.errors=[];this.validate();return this.errors.length==0},validate:function(){return this}},e);return f}; +Model.Collection=function(g){var e=function(b){this.collection=b||[]},f=function(b){return new e(b)};e.prototype=$.extend({add:function(){for(var b=0;b-1){this.collection.splice(b,1);return true}else return false},select:function(b){var a=_.select(this.collection,function(d,c){return b.call(d,c)});return f(a)},sort:function(b){var a=_.sortBy(this.collection,function(d, +c){return b.call(d,c)});return f(a)}},g);return new e}; +Model.RestPersistence=function(g,e){var f=function(){this.resource=g};f.prototype=$.extend({create:function(b,a){return this.xhr("POST",this.create_path(b),b,function(d,c){this.merge(c);a&&a.apply(this,arguments)})},create_path:function(){return this.resource},destroy:function(b,a){return this.xhr("DELETE",this.destroy_path(b),null,a)},destroy_path:function(b){return this.update_path(b)},update:function(b,a){return this.xhr("PUT",this.update_path(b),b,function(d,c){this.merge(c);a&&a.apply(this,arguments)})}, +update_path:function(b){return[this.resource,b.id()].join("/")},xhr:function(b,a,d,c){return $.ajax({type:b,url:a,dataType:"json",data:d?d.toParam():null,failure:function(h,j,i){c&&c.call(d,false,h,i)},success:function(h,j,i){c&&c.call(d,true,h,i)}})}},e);return new f}; diff --git a/src/constants.yml b/src/constants.yml index b4a2ad6..bc2c1cc 100644 --- a/src/constants.yml +++ b/src/constants.yml @@ -1 +1 @@ -VERSION: 0.6.0 +VERSION: 0.6.1