From 6b79a38453393216a02d72d2002042d9ce55428a Mon Sep 17 00:00:00 2001 From: Erik Bakstad Date: Tue, 29 Mar 2011 18:12:32 +0200 Subject: [PATCH 1/5] Missing json dep in Gemfile --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index bb20fcf..b122635 100644 --- a/Gemfile +++ b/Gemfile @@ -6,3 +6,4 @@ gem 'highlight', :require => 'simplabs/highlight' gem 'nokogiri' gem 'rdiscount' gem 'sinatra' +gem 'json' diff --git a/Gemfile.lock b/Gemfile.lock index ec717ee..f7372d8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,7 @@ GEM rack highlight (1.1.2) activesupport (>= 2.0.0) + json (1.5.1) nokogiri (1.4.4) rack (1.2.1) rdiscount (1.6.8) @@ -22,6 +23,7 @@ DEPENDENCIES closure-compiler fewer (~> 0.2.0) highlight + json nokogiri rdiscount sinatra From 4422ccd55aa0c8f92cf0058ba1358d792b748210 Mon Sep 17 00:00:00 2001 From: Erik Bakstad Date: Tue, 29 Mar 2011 20:45:42 +0200 Subject: [PATCH 2/5] Missing pygmentize in Gemfile --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index b122635..4cf6029 100644 --- a/Gemfile +++ b/Gemfile @@ -7,3 +7,4 @@ gem 'nokogiri' gem 'rdiscount' gem 'sinatra' gem 'json' +gem 'pygmentize' diff --git a/Gemfile.lock b/Gemfile.lock index f7372d8..6f29ad4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,6 +9,7 @@ GEM activesupport (>= 2.0.0) json (1.5.1) nokogiri (1.4.4) + pygmentize (0.0.2) rack (1.2.1) rdiscount (1.6.8) sinatra (1.1.3) @@ -25,5 +26,6 @@ DEPENDENCIES highlight json nokogiri + pygmentize rdiscount sinatra From ae245fbf90a3b91396e522468fb0f44ce9cfbcdd Mon Sep 17 00:00:00 2001 From: Erik Bakstad Date: Thu, 31 Mar 2011 13:33:28 +0200 Subject: [PATCH 3/5] Add Model.Coerce to allow attributeCoercion --- lib/bundler.rb | 1 + src/model.js | 2 +- src/model_class_methods.js | 7 ++++++ src/model_coerce.js | 38 ++++++++++++++++++++++++++++++ src/model_instance_methods.js | 19 ++++++++++++++- test/tests/model.js | 37 +++++++++++++++++++++++++++++ test/tests/model_coerce.js | 32 +++++++++++++++++++++++++ test/tests/model_rest.js | 44 ++++++++++++++++++++++++++++++++--- test/views/index.erb | 1 + 9 files changed, 176 insertions(+), 5 deletions(-) create mode 100644 src/model_coerce.js create mode 100644 test/tests/model_coerce.js diff --git a/lib/bundler.rb b/lib/bundler.rb index 04ca529..b76b76c 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -37,6 +37,7 @@ def files model_log model_module model_rest + model_coerce model_uid model_version ) diff --git a/src/model.js b/src/model.js index df954a5..f6065cf 100644 --- a/src/model.js +++ b/src/model.js @@ -1,7 +1,7 @@ var Model = function(name, func) { // The model constructor. var model = function(attributes) { - this.attributes = jQuery.extend({}, attributes) + this.attributes = this.coerceAttributes(jQuery.extend({}, attributes)) this.changes = {}; this.errors = new Model.Errors(this); this.uid = [name, Model.UID.generate()].join("-") diff --git a/src/model_class_methods.js b/src/model_class_methods.js index c2e86a1..d2487fc 100644 --- a/src/model_class_methods.js +++ b/src/model_class_methods.js @@ -103,6 +103,13 @@ Model.ClassMethods = { } }, + coerce: function(adapter, attributeTypes) { + if (arguments.length > 0) { + this.coerce.adapter = adapter + this.coerce.attributeTypes = attributeTypes + } + }, + pluck: function(attribute) { var all = this.all() var plucked = [] diff --git a/src/model_coerce.js b/src/model_coerce.js new file mode 100644 index 0000000..e93393e --- /dev/null +++ b/src/model_coerce.js @@ -0,0 +1,38 @@ +Model.Coerce = (function() { + return { + integer: function(value) { + return parseInt(value) + }, + 'boolean': function(value) { + return (!(value === false || value === "false" || + value === 0 || value === "0")); + }, + 'float': function(value) { + return parseFloat(value) + }, + isoDate : function(value) { + //http://zetafleet.com/blog/javascript-dateparse-for-iso-8601 + var origParse = Date.parse; + + function parseDate(date) { + var timestamp = origParse(date), minutesOffset = 0, struct; + if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?))?/.exec(date))) { + if (struct[8] !== 'Z') { + minutesOffset = +struct[10] * 60 + (+struct[11]); + + if (struct[9] === '+') { + minutesOffset = 0 - minutesOffset; + } + } + + timestamp = Date(Date.UTC(+struct[1], +struct[2] - 1, +struct[3], +struct[4], +struct[5] + minutesOffset, +struct[6], +struct[7].substr(0, 3))); + } + + return timestamp; + } + + return new Date(parseDate(value)) + } + } +})(); + diff --git a/src/model_instance_methods.js b/src/model_instance_methods.js index 986a75c..c40213e 100644 --- a/src/model_instance_methods.js +++ b/src/model_instance_methods.js @@ -13,7 +13,7 @@ Model.InstanceMethods = { // Clean up any stale changes. delete this.changes[name]; } else { - this.changes[name] = value; + this.changes[name] = this.coerceAttribute(name, value); } return this; } else if (typeof name === "object") { @@ -119,5 +119,22 @@ Model.InstanceMethods = { validate: function() { return this; + }, + + coerceAttribute: function(name, value) { + var adapter = this.constructor.coerce.adapter; + var attributeTypes = this.constructor.coerce.attributeTypes; + if (!(adapter === undefined || attributeTypes === undefined || attributeTypes[name] === undefined)) { + return adapter[attributeTypes[name]](value); + } else { + return value + } + }, + + coerceAttributes: function(attributes) { + for (var name in attributes) { + attributes[name] = this.coerceAttribute(name, attributes[name]) + } + return attributes } }; diff --git a/test/tests/model.js b/test/tests/model.js index 48b5d28..98eb00e 100644 --- a/test/tests/model.js +++ b/test/tests/model.js @@ -82,6 +82,43 @@ test("attr, attributes, changes, reset, save, destroy", function() { }); }); +test("attribute types with coercion", function() { + var Post = Model("post", function() { + this.coerce(Model.Coerce, + { + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) + }) + + var post = new Post({ + category_id: "1.4", + lat: "12345.6789", + published: "false", + pubDate: "2010-08-21T02:03:44Z" + }) + + equals(post.attr("category_id"), 1) + equals(post.attr("lat"), 12345.6789) + equals(post.attr("published"), false) + equals(post.attr("pubDate"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") + + post.attr("published", true) + equals(post.attr("published"), true) + + post.attr("pubDate", "2011-12-31T00:00:00Z") + ok(post.attributes.pubDate instanceof Date, "Should be instance of date!") + equals(post.attr("pubDate"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") + + post.attr({pubDate: "2010-01-01T00:00:0Z", category_id: 2, lat: 1.2, published: true}) + equals(post.attr("category_id"), 2) + equals(post.attr("lat"), 1.2) + equals(post.attr("published"), true) + equals(post.attr("pubDate"), "Fri Jan 01 2010 01:00:00 GMT+0100 (CET)") +}); + test("custom methods", function() { var Post = Model("post", function(klass, proto) { this.foo = function() { return "foo" } diff --git a/test/tests/model_coerce.js b/test/tests/model_coerce.js new file mode 100644 index 0000000..6615ce0 --- /dev/null +++ b/test/tests/model_coerce.js @@ -0,0 +1,32 @@ +module("Model.Coerce") + +test("Integer coercion", function() { + equals(Model.Coerce.integer("1"), 1) + equals(Model.Coerce.integer("1"), parseInt("1")) + equals(Model.Coerce.integer("4.5"), parseInt("4")) + equals(Model.Coerce.integer("4.5"), parseInt("4")) + same(Model.Coerce.integer("abc"), NaN) +}); + +test("Boolean coercion", function() { + equals(Model.Coerce.boolean("1"), true) + equals(Model.Coerce.boolean("0"), false) + equals(Model.Coerce.boolean("true"), true) + equals(Model.Coerce.boolean("false"), false) + equals(Model.Coerce.boolean(true), true) + equals(Model.Coerce.boolean(false), false) +}); + +test("Float coercion", function() { + equals(Model.Coerce.float("1.23"), 1.23) + equals(Model.Coerce.float("1"), 1.0) + same(Model.Coerce.float("abc"), NaN) +}); + +test("IsoDate coercion", function() { + equals(Model.Coerce.isoDate("2010-08-21T02:03:44Z"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") + equals(Model.Coerce.isoDate("test"), "Invalid Date") + ok(Model.Coerce.isoDate("2011-12-31T00:00:00Z") instanceof Date) + equals(Model.Coerce.isoDate("2011-12-31T00:00:00Z"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") +}); + diff --git a/test/tests/model_rest.js b/test/tests/model_rest.js index ba49df9..93eba3d 100644 --- a/test/tests/model_rest.js +++ b/test/tests/model_rest.js @@ -47,11 +47,49 @@ test("create with named params in resource path", function() { equals(AjaxSpy.requests.length, 1, "one request should have been made"); var request = AjaxSpy.requests.shift(); - + equals(request.type, "POST"); equals(request.url, "/root/3/nested/2/posts"); }); + +test("create types with coercion should stringify types correctly", function() { + var Post = Model("post", function() { + this.persistence(Model.REST, "/posts") + this.coerce(Model.Coerce, + { + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) + }) + + var post = new Post({ + category_id: "1.4", + lat: "12345.6789", + published: "false", + pubDate: "2010-08-21T02:03:44Z" + }) + + AjaxSpy.start(); + stop(); + + post.save(function(success) { + ok(success); + start(); + }); + + equals(AjaxSpy.requests.length, 1, "one request should have been made"); + + var request = AjaxSpy.requests.shift(); + + equals(request.type, "POST"); + equals(request.url, "/posts"); + equals(request.data, "{\"post\":{\"category_id\":1,\"lat\":12345.6789,\"published\":false,\"pubDate\":\"2010-08-21T02:03:44.000Z\"}}") + +}); + test("update with named params in resource path", function() { var Post = Model("post", function() { this.persistence(Model.REST, "/root/:root_id/nested/:nested_id/posts") @@ -190,13 +228,13 @@ test("create failure", function() { }); test("create with AjaxSetup", function() { - + jQuery.ajaxSetup({ data: { socket_id: '111' } }) - + var Post = Model("post", function() { this.persistence(Model.REST, "/posts") }); diff --git a/test/views/index.erb b/test/views/index.erb index 285fa8e..2871454 100644 --- a/test/views/index.erb +++ b/test/views/index.erb @@ -24,6 +24,7 @@ + From a43f81b6b36ee04b776487ce2ca91511dcf70e66 Mon Sep 17 00:00:00 2001 From: Erik Bakstad Date: Fri, 1 Apr 2011 13:56:05 +0200 Subject: [PATCH 4/5] Fix isoDate bug in IE --- src/model_coerce.js | 36 +++++++++++++++++++++--------------- test/tests/model_rest.js | 18 +++++++++--------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/model_coerce.js b/src/model_coerce.js index e93393e..185e30a 100644 --- a/src/model_coerce.js +++ b/src/model_coerce.js @@ -12,26 +12,32 @@ Model.Coerce = (function() { }, isoDate : function(value) { //http://zetafleet.com/blog/javascript-dateparse-for-iso-8601 - var origParse = Date.parse; + /** + * Date.parse with progressive enhancement for ISO-8601, version 2 + * © 2010 Colin Snover + * Released under MIT license. + */ + (function () { + var origParse = Date.parse; + Date.parse = function (date) { + var timestamp = origParse(date), minutesOffset = 0, struct; + if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?))?/.exec(date))) { + if (struct[8] !== 'Z') { + minutesOffset = +struct[10] * 60 + (+struct[11]); - function parseDate(date) { - var timestamp = origParse(date), minutesOffset = 0, struct; - if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?))?/.exec(date))) { - if (struct[8] !== 'Z') { - minutesOffset = +struct[10] * 60 + (+struct[11]); - - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; + if (struct[9] === '+') { + minutesOffset = 0 - minutesOffset; + } } - } - timestamp = Date(Date.UTC(+struct[1], +struct[2] - 1, +struct[3], +struct[4], +struct[5] + minutesOffset, +struct[6], +struct[7].substr(0, 3))); - } + timestamp = Date.UTC(+struct[1], +struct[2] - 1, +struct[3], +struct[4], +struct[5] + minutesOffset, +struct[6], +struct[7].substr(0, 3)); + } - return timestamp; - } + return timestamp; + }; + }()); - return new Date(parseDate(value)) + return new Date(Date.parse(value)) } } })(); diff --git a/test/tests/model_rest.js b/test/tests/model_rest.js index 93eba3d..cdb0534 100644 --- a/test/tests/model_rest.js +++ b/test/tests/model_rest.js @@ -54,16 +54,16 @@ test("create with named params in resource path", function() { test("create types with coercion should stringify types correctly", function() { - var Post = Model("post", function() { - this.persistence(Model.REST, "/posts") - this.coerce(Model.Coerce, - { - category_id: "integer", - lat: "float", - published: "boolean", - pubDate: "isoDate" + var Post = Model("post", function() { + this.persistence(Model.REST, "/posts"), + this.coerce(Model.Coerce, + { + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) }) - }) var post = new Post({ category_id: "1.4", From f06bdd08ff02bdd9ac041f4999ed6c438721b5c1 Mon Sep 17 00:00:00 2001 From: Erik Bakstad Date: Tue, 12 Apr 2011 15:25:59 +0200 Subject: [PATCH 5/5] Refactored Coercion to use new plugin architecture --- lib/bundler.rb | 2 +- src/model.js | 2 +- src/model_class_methods.js | 7 -- src/model_coerce.js | 44 ---------- src/model_instance_methods.js | 27 ++---- src/plugin_coerce.js | 91 ++++++++++++++++++++ test/tests/model_rest_test.js | 37 -------- test/tests/model_test.js | 37 -------- test/tests/plugin_coerce.js | 32 ------- test/tests/plugin_coerce_test.js | 139 +++++++++++++++++++++++++++++++ 10 files changed, 241 insertions(+), 177 deletions(-) delete mode 100644 src/model_coerce.js create mode 100644 src/plugin_coerce.js delete mode 100644 test/tests/plugin_coerce.js create mode 100644 test/tests/plugin_coerce_test.js diff --git a/lib/bundler.rb b/lib/bundler.rb index 1c5906d..6e188ea 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -37,10 +37,10 @@ def files model_log model_module model_rest - model_coerce model_uid model_version model_base + plugin_coerce ) end diff --git a/src/model.js b/src/model.js index a0dc211..f9ac844 100644 --- a/src/model.js +++ b/src/model.js @@ -1,7 +1,7 @@ var Model = function(name, func) { // The model constructor. var model = function(attributes) { - this.attributes = this.coerceAttributes(jQuery.extend({}, attributes)) + this.attributes = this.setAttributeValues(jQuery.extend({}, attributes)) this.changes = {}; this.errors = new Model.Errors(this); this.uid = [name, Model.UID.generate()].join("-") diff --git a/src/model_class_methods.js b/src/model_class_methods.js index ce065f5..bd747f4 100644 --- a/src/model_class_methods.js +++ b/src/model_class_methods.js @@ -96,13 +96,6 @@ Model.ClassMethods = { } }, - coerce: function(adapter, attributeTypes) { - if (arguments.length > 0) { - this.coerce.adapter = adapter - this.coerce.attributeTypes = attributeTypes - } - }, - pluck: function(attribute) { var all = this.all() var plucked = [] diff --git a/src/model_coerce.js b/src/model_coerce.js deleted file mode 100644 index 185e30a..0000000 --- a/src/model_coerce.js +++ /dev/null @@ -1,44 +0,0 @@ -Model.Coerce = (function() { - return { - integer: function(value) { - return parseInt(value) - }, - 'boolean': function(value) { - return (!(value === false || value === "false" || - value === 0 || value === "0")); - }, - 'float': function(value) { - return parseFloat(value) - }, - isoDate : function(value) { - //http://zetafleet.com/blog/javascript-dateparse-for-iso-8601 - /** - * Date.parse with progressive enhancement for ISO-8601, version 2 - * © 2010 Colin Snover - * Released under MIT license. - */ - (function () { - var origParse = Date.parse; - Date.parse = function (date) { - var timestamp = origParse(date), minutesOffset = 0, struct; - if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?))?/.exec(date))) { - if (struct[8] !== 'Z') { - minutesOffset = +struct[10] * 60 + (+struct[11]); - - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; - } - } - - timestamp = Date.UTC(+struct[1], +struct[2] - 1, +struct[3], +struct[4], +struct[5] + minutesOffset, +struct[6], +struct[7].substr(0, 3)); - } - - return timestamp; - }; - }()); - - return new Date(Date.parse(value)) - } - } -})(); - diff --git a/src/model_instance_methods.js b/src/model_instance_methods.js index c40213e..a5f40dc 100644 --- a/src/model_instance_methods.js +++ b/src/model_instance_methods.js @@ -3,6 +3,14 @@ Model.InstanceMethods = { return this.attr() }, + setAttributeValue: function(name, value) { + return value + }, + + setAttributeValues: function(attributes) { + return attributes + }, + attr: function(name, value) { if (arguments.length === 0) { // Combined attributes/changes object. @@ -13,7 +21,7 @@ Model.InstanceMethods = { // Clean up any stale changes. delete this.changes[name]; } else { - this.changes[name] = this.coerceAttribute(name, value); + this.changes[name] = this.setAttributeValue(name, value) } return this; } else if (typeof name === "object") { @@ -119,22 +127,5 @@ Model.InstanceMethods = { validate: function() { return this; - }, - - coerceAttribute: function(name, value) { - var adapter = this.constructor.coerce.adapter; - var attributeTypes = this.constructor.coerce.attributeTypes; - if (!(adapter === undefined || attributeTypes === undefined || attributeTypes[name] === undefined)) { - return adapter[attributeTypes[name]](value); - } else { - return value - } - }, - - coerceAttributes: function(attributes) { - for (var name in attributes) { - attributes[name] = this.coerceAttribute(name, attributes[name]) - } - return attributes } }; diff --git a/src/plugin_coerce.js b/src/plugin_coerce.js new file mode 100644 index 0000000..b6c18a6 --- /dev/null +++ b/src/plugin_coerce.js @@ -0,0 +1,91 @@ +if (!(typeof Plugin === 'function')) { + Plugin = function() { + } +} + +Plugin.Coerce = (function() { + + var plugin = function (klass, aTypes) { + var attributeTypes = aTypes; + + var augmentSetAttributeValue = function(orig_fn) { + return function() { + var name = arguments[0] + var value = arguments[1] + return coerceAttribute(name, orig_fn.call(this, name, value)) + } + } + + var augmentSetAttributeValues = function(orig_fn) { + return function() { + return coerceAttributes(orig_fn.apply(this, arguments)) + } + } + + function coerceAttribute(name, value) { + if (!(attributeTypes === undefined || attributeTypes[name] === undefined)) { + return plugin[attributeTypes[name]](value); + } else { + return value + } + } + + function coerceAttributes(attributes) { + for (var name in attributes) { + attributes[name] = coerceAttribute(name, attributes[name]) + } + return attributes + } + + klass.prototype.setAttributeValue = augmentSetAttributeValue(klass.prototype.setAttributeValue) + klass.prototype.setAttributeValues = augmentSetAttributeValues(klass.prototype.setAttributeValues) + } + + plugin.integer = function(value) { + return parseInt(value) + } + + plugin['boolean'] = function(value) { + return (!(value === false || value === "false" || + value === 0 || value === "0")); + } + + plugin['float'] = function(value) { + return parseFloat(value) + } + + plugin.isoDate = function(value) { + //http://zetafleet.com/blog/javascript-dateparse-for-iso-8601 + /** + * Date.parse with progressive enhancement for ISO-8601, version 2 + * © 2010 Colin Snover + * Released under MIT license. + */ + (function () { + var origParse = Date.parse; + Date.parse = function (date) { + var timestamp = origParse(date), minutesOffset = 0, struct; + if (isNaN(timestamp) && (struct = /^(\d{4}|[+\-]\d{6})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3,}))?)?(?:(Z)|([+\-])(\d{2})(?::?(\d{2}))?))?/.exec(date))) { + if (struct[8] !== 'Z') { + minutesOffset = +struct[10] * 60 + (+struct[11]); + + if (struct[9] === '+') { + minutesOffset = 0 - minutesOffset; + } + } + + timestamp = Date.UTC(+struct[1], +struct[2] - 1, +struct[3], +struct[4], +struct[5] + minutesOffset, +struct[6], +struct[7].substr(0, 3)); + } + + return timestamp; + }; + }()); + + return new Date(Date.parse(value)) + } + + return plugin + +})(); + + diff --git a/test/tests/model_rest_test.js b/test/tests/model_rest_test.js index 2d62bac..d1a122e 100644 --- a/test/tests/model_rest_test.js +++ b/test/tests/model_rest_test.js @@ -53,43 +53,6 @@ test("create with named params in resource path", function() { }); -test("create types with coercion should stringify types correctly", function() { - var Post = Model("post", function() { - this.persistence(Model.REST, "/posts"), - this.coerce(Model.Coerce, - { - category_id: "integer", - lat: "float", - published: "boolean", - pubDate: "isoDate" - }) - }) - - var post = new Post({ - category_id: "1.4", - lat: "12345.6789", - published: "false", - pubDate: "2010-08-21T02:03:44Z" - }) - - AjaxSpy.start(); - stop(); - - post.save(function(success) { - ok(success); - start(); - }); - - equals(AjaxSpy.requests.length, 1, "one request should have been made"); - - var request = AjaxSpy.requests.shift(); - - equals(request.type, "POST"); - equals(request.url, "/posts"); - equals(request.data, "{\"post\":{\"category_id\":1,\"lat\":12345.6789,\"published\":false,\"pubDate\":\"2010-08-21T02:03:44.000Z\"}}") - -}); - test("update with named params in resource path", function() { var Post = Model("post", function() { this.persistence(Model.REST, "/root/:root_id/nested/:nested_id/posts") diff --git a/test/tests/model_test.js b/test/tests/model_test.js index 6a86a40..7e56e0d 100644 --- a/test/tests/model_test.js +++ b/test/tests/model_test.js @@ -82,43 +82,6 @@ test("attr, attributes, changes, reset, save, destroy", function() { }); }); -test("attribute types with coercion", function() { - var Post = Model("post", function() { - this.coerce(Model.Coerce, - { - category_id: "integer", - lat: "float", - published: "boolean", - pubDate: "isoDate" - }) - }) - - var post = new Post({ - category_id: "1.4", - lat: "12345.6789", - published: "false", - pubDate: "2010-08-21T02:03:44Z" - }) - - equals(post.attr("category_id"), 1) - equals(post.attr("lat"), 12345.6789) - equals(post.attr("published"), false) - equals(post.attr("pubDate"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") - - post.attr("published", true) - equals(post.attr("published"), true) - - post.attr("pubDate", "2011-12-31T00:00:00Z") - ok(post.attributes.pubDate instanceof Date, "Should be instance of date!") - equals(post.attr("pubDate"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") - - post.attr({pubDate: "2010-01-01T00:00:0Z", category_id: 2, lat: 1.2, published: true}) - equals(post.attr("category_id"), 2) - equals(post.attr("lat"), 1.2) - equals(post.attr("published"), true) - equals(post.attr("pubDate"), "Fri Jan 01 2010 01:00:00 GMT+0100 (CET)") -}); - test("custom methods", function() { var Post = Model("post", function(klass, proto) { this.foo = function() { return "foo" } diff --git a/test/tests/plugin_coerce.js b/test/tests/plugin_coerce.js deleted file mode 100644 index 6615ce0..0000000 --- a/test/tests/plugin_coerce.js +++ /dev/null @@ -1,32 +0,0 @@ -module("Model.Coerce") - -test("Integer coercion", function() { - equals(Model.Coerce.integer("1"), 1) - equals(Model.Coerce.integer("1"), parseInt("1")) - equals(Model.Coerce.integer("4.5"), parseInt("4")) - equals(Model.Coerce.integer("4.5"), parseInt("4")) - same(Model.Coerce.integer("abc"), NaN) -}); - -test("Boolean coercion", function() { - equals(Model.Coerce.boolean("1"), true) - equals(Model.Coerce.boolean("0"), false) - equals(Model.Coerce.boolean("true"), true) - equals(Model.Coerce.boolean("false"), false) - equals(Model.Coerce.boolean(true), true) - equals(Model.Coerce.boolean(false), false) -}); - -test("Float coercion", function() { - equals(Model.Coerce.float("1.23"), 1.23) - equals(Model.Coerce.float("1"), 1.0) - same(Model.Coerce.float("abc"), NaN) -}); - -test("IsoDate coercion", function() { - equals(Model.Coerce.isoDate("2010-08-21T02:03:44Z"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") - equals(Model.Coerce.isoDate("test"), "Invalid Date") - ok(Model.Coerce.isoDate("2011-12-31T00:00:00Z") instanceof Date) - equals(Model.Coerce.isoDate("2011-12-31T00:00:00Z"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") -}); - diff --git a/test/tests/plugin_coerce_test.js b/test/tests/plugin_coerce_test.js new file mode 100644 index 0000000..2a493dd --- /dev/null +++ b/test/tests/plugin_coerce_test.js @@ -0,0 +1,139 @@ +module("Plugin.Coerce") + +test("Integer coercion", function() { + equals(Plugin.Coerce.integer("1"), 1) + equals(Plugin.Coerce.integer("1"), parseInt("1")) + equals(Plugin.Coerce.integer("4.5"), parseInt("4")) + equals(Plugin.Coerce.integer("4.5"), parseInt("4")) + same(Plugin.Coerce.integer("abc"), NaN) +}); + +test("Boolean coercion", function() { + equals(Plugin.Coerce.boolean("1"), true) + equals(Plugin.Coerce.boolean("0"), false) + equals(Plugin.Coerce.boolean("true"), true) + equals(Plugin.Coerce.boolean("false"), false) + equals(Plugin.Coerce.boolean(true), true) + equals(Plugin.Coerce.boolean(false), false) +}); + +test("Float coercion", function() { + equals(Plugin.Coerce.float("1.23"), 1.23) + equals(Plugin.Coerce.float("1"), 1.0) + same(Plugin.Coerce.float("abc"), NaN) +}); + +test("IsoDate coercion", function() { + equals(Plugin.Coerce.isoDate("2010-08-21T02:03:44Z"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") + equals(Plugin.Coerce.isoDate("test"), "Invalid Date") + ok(Plugin.Coerce.isoDate("2011-12-31T00:00:00Z") instanceof Date) + equals(Plugin.Coerce.isoDate("2011-12-31T00:00:00Z"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") +}); + + +test("attribute types with coercion", function() { + var Post = Model("post", function() { + this.use(Plugin.Coerce, + { + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) + }) + + var post = new Post({ + category_id: "1.4", + lat: "12345.6789", + published: "false", + pubDate: "2010-08-21T02:03:44Z" + }) + + equals(post.attr("category_id"), 1) + equals(post.attr("lat"), 12345.6789) + equals(post.attr("published"), false) + equals(post.attr("pubDate"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") + + post.attr("published", true) + equals(post.attr("published"), true) + + post.attr("pubDate", "2011-12-31T00:00:00Z") + ok(post.attributes.pubDate instanceof Date, "Should be instance of date!") + equals(post.attr("pubDate"), "Sat Dec 31 2011 01:00:00 GMT+0100 (CET)") + + post.attr({pubDate: "2010-01-01T00:00:0Z", category_id: 2, lat: 1.2, published: true}) + equals(post.attr("category_id"), 2) + equals(post.attr("lat"), 1.2) + equals(post.attr("published"), true) + equals(post.attr("pubDate"), "Fri Jan 01 2010 01:00:00 GMT+0100 (CET)") +}); + + +test("create types with coercion should stringify types correctly", function() { + var Post = Model("post", function() { + this.persistence(Model.REST, "/posts"), + this.use(Plugin.Coerce, + { + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) + }) + + var post = new Post({ + category_id: "1.4", + lat: "12345.6789", + published: "false", + pubDate: "2010-08-21T02:03:44Z" + }) + + AjaxSpy.start(); + stop(); + + post.save(function(success) { + ok(success); + start(); + }); + + equals(AjaxSpy.requests.length, 1, "one request should have been made"); + + var request = AjaxSpy.requests.shift(); + + equals(request.type, "POST"); + equals(request.url, "/posts"); + equals(request.data, "{\"post\":{\"category_id\":1,\"lat\":12345.6789,\"published\":false,\"pubDate\":\"2010-08-21T02:03:44.000Z\"}}") + +}); + +test("Custom coercions", function() { + Plugin.Coerce.uppercase = function(value) { + return value.toUpperCase() + } + + var Post = Model("post", function() { + this.use(Plugin.Coerce, + { + title: "uppercase", + category_id: "integer", + lat: "float", + published: "boolean", + pubDate: "isoDate" + }) + }) + + var post = new Post({ + title: "title", + category_id: "1.4", + lat: "12345.6789", + published: "false", + pubDate: "2010-08-21T02:03:44Z" + }) + + equals(post.attr("title"), "TITLE") + equals(post.attr("category_id"), 1) + equals(post.attr("lat"), 12345.6789) + equals(post.attr("published"), false) + equals(post.attr("pubDate"), "Sat Aug 21 2010 04:03:44 GMT+0200 (CEST)") + +});