Permalink
Browse files

Version 0.6.0.

  • Loading branch information...
1 parent da44df0 commit f10500697691a5ff9930ce1b64a99019eb7854ac @benpickles committed Feb 11, 2010
View
16 CHANGELOG.markdown
@@ -0,0 +1,16 @@
+# Changelog
+
+## 0.6.0
+
+* Pass only a single callback to save/destroy which is called with a boolean to indicate success/failure.
+* Bundle release into a single, versioned Javascript file plus minified version.
+* Change behaviour of `Model.Collection` and don't let duplicates (scoped by id) of the same model to be stored. [Laurie Young]
+
+## 0.5.1
+
+* Fix that setting a null value with `attr` should be read back correctly from `changes`.
+* `errors` array shouldn't have to be manually `reset` when implementing `validate`.
+
+## 0.5.0
+
+* First tagged release.
View
283 dist/js-model-0.6.0.js
@@ -0,0 +1,283 @@
+/* js-model JavaScript library, version 0.6.0
+ * (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;
+ },
+
+ newRecord: function() {
+ return this.id() == null;
+ },
+
+ reset: function() {
+ this.changes = {};
+ return this;
+ },
+
+ save: function(callback) {
+ if (!this.valid()) return false;
+
+ this.update(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) {
+ $.extend(this.attributes, attributes);
+ 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.update(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.update(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();
+};
View
14 dist/js-model-0.6.0.min.js
@@ -0,0 +1,14 @@
+/* js-model JavaScript library, version 0.6.0
+ * (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){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},newRecord:function(){return this.id()==null},reset:function(){this.changes=
+{};return this},save:function(a){if(!this.valid())return false;this.update(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){$.extend(this.attributes,a);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<arguments.length;b++){var a=arguments[b];this.detect(function(){return this.id()!=null&&this.id()==a.id()})||this.collection.push(arguments[b])}return this},all:function(){return this.collection},detect:function(b){return _.detect(this.collection,function(a,d){return b.call(a,d)})||null},each:function(b){$.each(this.collection,function(a){b.call(this,
+a)});return this},find:function(b){return this.detect(function(){return this.id()==b})},first:function(){return this.collection[0]||null},last:function(){return this.collection[this.collection.length-1]||null},remove:function(b){var a=_.invoke(this.collection,"id");b=_.indexOf(a,b);if(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.update(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.update(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};
View
4 dist/model.min.js
@@ -1,4 +0,0 @@
-var Model=function(j,e){var g=function(a){this._name=j;this.attributes=a||{};this.changes={};this.collection=f;this.errors=[];this.trigger("initialize")},f;if(e&&e.collection){f=e.collection;delete e.collection}else f=Model.Collection();g=$.extend(g,f);g.prototype=$.extend({attr:function(a,b){if(arguments.length==2){if(_.isEqual(this.attributes[a],b))delete this.changes[a];else this.changes[a]=b;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,b,c){var d=this,h=function(){if(d.collection)if(a=="create")d.collection.add(d);else a=="destroy"&&d.collection.remove(d.id())};if(this.persistence)this.persistence[a](this,function(){h();var i;if(b)i=b.apply(d,arguments);d.trigger(a);return i},c);else{h();this.trigger(a)}},destroy:function(a,b){this.callPersistMethod("destroy",a,b);return this},id:function(){return this.attributes.id||null},newRecord:function(){return this.id()==null},
-reset:function(){this.changes={};return this},save:function(a,b){if(!this.valid())return false;this.update(this.changes).reset();this.callPersistMethod(this.newRecord()?"create":"update",a,b);return true},toParam:function(){var a={};for(var b in this.attributes){var c=this.attributes[b];if(b!="id"&&c!=null)a[this._name+"["+b+"]"]=c}return a},trigger:function(a){$(document).trigger([a,this._name].join("."),[this]);return this},update:function(a){$.extend(this.attributes,a);return this},valid:function(){this.errors=
-[];this.validate();return this.errors.length==0},validate:function(){return this}},e);return g};
View
2 dist/model_collection.min.js
@@ -1,2 +0,0 @@
-Model.Collection=function(g){var d=function(a){this.collection=a||[]},f=function(a){return new d(a)};d.prototype=$.extend({add:function(){for(var a=0;a<arguments.length;a++)this.collection.push(arguments[a]);return this},all:function(){return this.collection},detect:function(a){return _.detect(this.collection,function(b,c){return a.call(b,c)})||null},each:function(a){$.each(this.collection,function(b){a.call(this,b)});return this},find:function(a){return this.detect(function(){return this.id()==a})},
-first:function(){return this.collection[0]||null},last:function(){return this.collection[this.collection.length-1]||null},remove:function(a){var b=_.invoke(this.collection,"id");a=_.indexOf(b,a);if(a>-1){this.collection.splice(a,1);return true}else return false},select:function(a){var b=_.select(this.collection,function(c,e){return a.call(c,e)});return f(b)},sort:function(a){var b=_.sortBy(this.collection,function(c,e){return a.call(c,e)});return f(b)}},g);return new d};
View
2 dist/model_rest_persistence.min.js
@@ -1,2 +0,0 @@
-Model.RestPersistence=function(g,h){var e=function(){this.resource=g};e.prototype=$.extend({create:function(a,c,b){return this.xhr("POST",this.create_path(a),a,function(d){this.update(d);c&&c.apply(this,arguments)},b)},create_path:function(){return this.resource},destroy:function(a,c,b){return this.xhr("DELETE",this.update_path(a),null,c,b)},update:function(a,c,b){return this.xhr("PUT",this.update_path(a),a,function(d){this.update(d);c&&c.apply(this,arguments)},b)},update_path:function(a){return[this.resource,
-a.id()].join("/")},xhr:function(a,c,b,d,f){return $.ajax({type:a,url:c,dataType:"json",data:b?b.toParam():null,failure:function(){f&&f.apply(b,arguments)},success:function(){d&&d.apply(b,arguments)}})}},h);return new e};
View
2 src/constants.yml
@@ -1 +1 @@
-VERSION: 0.5.1
+VERSION: 0.6.0

0 comments on commit f105006

Please sign in to comment.