From e56f968f6eda916f5b18ed68bee20a7a866718bc Mon Sep 17 00:00:00 2001 From: pahaz Date: Wed, 7 Nov 2012 12:36:55 +0600 Subject: [PATCH 1/7] init --- .gitignore | 3 +- .gitmodules | 3 ++ Collection.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++ Model.js | 53 +++++++++++++++++++++++++++ MyAbstractLayer.js | 85 +++++++++++++++++++++++++++++++++++++++++++ index.html | 6 +++ 6 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 Collection.js create mode 100644 Model.js create mode 100644 MyAbstractLayer.js create mode 100644 index.html diff --git a/.gitignore b/.gitignore index 62c8935..b50ef66 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.idea/ \ No newline at end of file +.idea/ +back* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..629c4db --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "underscore"] + path = underscore + url = git://github.com/documentcloud/underscore.git diff --git a/Collection.js b/Collection.js new file mode 100644 index 0000000..1211606 --- /dev/null +++ b/Collection.js @@ -0,0 +1,91 @@ +/** + * Коллекции для модели Event. + * + * @constructor + * @this {EventsCollection} + * @param {Array} collect_list Список Event классов. + */ +var EventsCollection = function (collect_list) { + if (Object.prototype.toString.call(collect_list) !== '[object Array]') { + throw new TypeError("argument must be Array"); + } + + this.list = collect_list; +}; + +/** + * Фильтрует объекты из коллекции. + * + * @param {function} filter_function Функция для фильтрации, this - ссылка на Event, должен вернуть boolean значение (True - значение будет добавлено в отфильтрованную коллекцию). + * @return {EventsCollection} Новый объект EventsCollection. + */ +EventsCollection.prototype.filter = function (filter_function) { + if (typeof filter_function !== "function") { + throw new TypeError("argument must be function"); + } + + var buff = []; + for (var i = 0; i < this.list.length; i++) { + var event = this.list[i]; + if (filter_function.call(event)) { + buff.push(event); + } + } + + return new EventsCollection(buff); +} + +/** + * Отфильтровывает события, которые произошли до указанной даты. + * + * @return {EventsCollection} Новый объект EventsCollection. + */ +EventsCollection.prototype.start_before = function(date) { + return this.filter(function () { + return this.info.start_time < date; + }); +}; + +/** + * Возвращает количество событий (Event) в коллекции. + * + * @return {number} Кол-во событий. + */ +EventsCollection.prototype.count_events = function() { + return this.list.length; +}; + +/** + * Отфильтровывает события, которые произошли после указанной даты. + * + * @return {EventsCollection} Новый объект EventsCollection. + */ +EventsCollection.prototype.start_after = function(date) { + return this.filter(function () { + return this.info.start_time > date; + }); +}; + + +function Collection_tests() { + var a1 = createNewEvent(222, 333, 'lol'); + var a2 = createNewEvent(223, 333, 'lol2'); + var a3 = createNewEvent(100, 500, 'lol2'); + var events_data = [a1, a2, a3]; + var events_callect = new EventsCollection(events_data); + + // t1 + var events_before_date = events_callect.start_before(223); + console.log(events_before_date.list.length == 2); + + // t2 + console.log(events_before_date.count_events() == 2); + + // t3 + var events_after_date = events_callect.start_after(222); + console.log(events_after_date.count_events() == 1); + + // t4 combine methods + var some_filtered_event = events_callect.start_before(223).start_after(100); + console.log(some_filtered_event.count_events() == 1); +} \ No newline at end of file diff --git a/Model.js b/Model.js new file mode 100644 index 0000000..2898e3f --- /dev/null +++ b/Model.js @@ -0,0 +1,53 @@ + +/** + * Модель для класса Event. + * + * @constructor + * @param {Object} info Обект, описывающий общую информацию о мероприятии (место проведения, название, описание, временные рамки). + */ +var Event = function (info) { + var def = { + title: "event" + }; + + this.info = _.extend(def, info); + + if (!this.info.start_time || !this.info.end_time) { + throw "miss required fields"; + } + + if (this.info.start_time > this.info.end_time) { + throw "starat_time more then end_time"; + } + +}; + +/** + * Создает объект Event + * + * @param {Number|Date} start Начало события + * @param {Number|Date} end Конец события + * @param {String} [name="Событие"] Имя события + * + * @example + * Event(new Date("2011-10-10T14:48:00"), + * new Date("2011-10-10T15:48:00"), + * "Совещание") + * + * @return {Object} + */ +function createNewEvent(start_at, end_at, name) { + var info = { + start_time: start_at, + end_time: end_at, + name: name + }; + + return new Event(info); +} + + +function Model_tests() { + var a = createNewEvent(222, 333, 'lol'); +} + \ No newline at end of file diff --git a/MyAbstractLayer.js b/MyAbstractLayer.js new file mode 100644 index 0000000..c6536a2 --- /dev/null +++ b/MyAbstractLayer.js @@ -0,0 +1,85 @@ +/** + * Created with PyCharm. + * User: stribog + * Date: 07.11.12 + * Time: 1:20 + * To change this template use File | Settings | File Templates. + */ + +// Model interface +var Model = function (attrs) {}; +Model.prototype.default = {}; +Model.prototype.constructor = function () { throw new SyntaxError("no implement"); }; // void +Model.prototype.get = function () { throw new SyntaxError("no implement"); }; // true / false +Model.prototype.set = function () { throw new SyntaxError("no implement"); }; // true / false +Model.prototype.has = function () { throw new SyntaxError("no implement"); }; // true / false +Model.prototype.validate = function () { throw new SyntaxError("no implement"); }; // true / false +//Model.prototype.extend = Model2; +//Model.prototype.super = function () {}; // void + +function TestModel() { + function ok(name, val1, val2) { + if (val1 !== val2) { + console.log(name + " [FAILED]"); + } else { + console.log(name + " [OK]"); + } + } + + var M0 = new Model({ + 'default': { + 'val0' : -1 + }, + 'constructor': function (v0) { + if (v0) { + this.set('val0', v0); + } + }, + 'validate': function () { + return this.get('val0') < 0; + } + }); + var M1 = new Model({ + 'default' : { + 'val1': 1, + 'val2': 2 + }, + 'constructor' : function (v0, v2) { + this.super(v0); + if (v2) { + this.set('val2', v2); + } + }, + 'validate' : function() { + return this.get('val1') < 5 && this.get('val2') < 5; + }, + 'extend' : M0 + }); + + var m = new M0(); + ok('Default', m.get('val0'), -1); + + m = new M0(-2); + ok('Constructor', m.get('val0'), -2); + + m = new M0(-3); + m.set('val9', -1); + ok('Set and Get', m.get('val9'), -1); + + m = new M0(8888); + m.set('val0', 9999); + ok('Validator', m.get('val0'), -1); + + m = new M1(); + ok('Parent Default', m.get('val0'), -1); + + m = new M1(-10, -20); + ok('Parent Constructor 1', m.get('val0'), -10); + ok('Parent Constructor 2', m.get('val2'), -20); + + m = new M1(); + m.set('val0', 9); + m.set('val1', 9); + ok('Parent Get and Set', m.get('val0'), -1); + ok('Parent Get and Set', m.get('val1'), 1); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..b2c35b2 --- /dev/null +++ b/index.html @@ -0,0 +1,6 @@ +

hello

+ + + + + From d4dfff37e7022b561c975442627cd4e6d2989356 Mon Sep 17 00:00:00 2001 From: pahaz Date: Sun, 11 Nov 2012 18:22:43 +0600 Subject: [PATCH 2/7] finall --- .gitignore | 1 - .gitmodules | 5 +- Collection.js | 61 ++---- Model.js | 36 ++-- MyAbstractLayer.js | 523 ++++++++++++++++++++++++++++++++++++++++----- backbone | 1 + index.html | 3 +- 7 files changed, 522 insertions(+), 108 deletions(-) create mode 160000 backbone diff --git a/.gitignore b/.gitignore index b50ef66..9f11b75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ .idea/ -back* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 629c4db..634930d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "underscore"] path = underscore - url = git://github.com/documentcloud/underscore.git + url = https://github.com/documentcloud/underscore.git +[submodule "backbone"] + path = backbone + url = https://github.com/documentcloud/backbone.git diff --git a/Collection.js b/Collection.js index 1211606..ad1b9bd 100644 --- a/Collection.js +++ b/Collection.js @@ -5,44 +5,18 @@ * @this {EventsCollection} * @param {Array} collect_list Список Event классов. */ -var EventsCollection = function (collect_list) { - if (Object.prototype.toString.call(collect_list) !== '[object Array]') { - throw new TypeError("argument must be Array"); - } - - this.list = collect_list; -}; +var EventsCollection = new Collection({ + 'model': Event +}); -/** - * Фильтрует объекты из коллекции. - * - * @param {function} filter_function Функция для фильтрации, this - ссылка на Event, должен вернуть boolean значение (True - значение будет добавлено в отфильтрованную коллекцию). - * @return {EventsCollection} Новый объект EventsCollection. - */ -EventsCollection.prototype.filter = function (filter_function) { - if (typeof filter_function !== "function") { - throw new TypeError("argument must be function"); - } - - var buff = []; - for (var i = 0; i < this.list.length; i++) { - var event = this.list[i]; - if (filter_function.call(event)) { - buff.push(event); - } - } - - return new EventsCollection(buff); -} - /** * Отфильтровывает события, которые произошли до указанной даты. * * @return {EventsCollection} Новый объект EventsCollection. */ EventsCollection.prototype.start_before = function(date) { - return this.filter(function () { - return this.info.start_time < date; + return this.filter(function (event) { + return event.get('start_time') < date; }); }; @@ -52,7 +26,7 @@ EventsCollection.prototype.start_before = function(date) { * @return {number} Кол-во событий. */ EventsCollection.prototype.count_events = function() { - return this.list.length; + return this.size(); }; /** @@ -61,12 +35,20 @@ EventsCollection.prototype.count_events = function() { * @return {EventsCollection} Новый объект EventsCollection. */ EventsCollection.prototype.start_after = function(date) { - return this.filter(function () { - return this.info.start_time > date; + return this.filter(function (event) { + return event.get('start_time') > date; }); }; +function ok(name, val1, val2) { + if (val1 !== val2) { + console.log(name + " [FAILED]"); + } else { + console.log(name + " [OK]"); + } +} + function Collection_tests() { var a1 = createNewEvent(222, 333, 'lol'); var a2 = createNewEvent(223, 333, 'lol2'); @@ -76,16 +58,17 @@ function Collection_tests() { // t1 var events_before_date = events_callect.start_before(223); - console.log(events_before_date.list.length == 2); + ok('events_before_date filter', events_before_date.size(), 2); // t2 - console.log(events_before_date.count_events() == 2); + ok('events_before_date count_events()', events_before_date.count_events(), 2); // t3 var events_after_date = events_callect.start_after(222); - console.log(events_after_date.count_events() == 1); + ok('events_after_date filter count_events()', events_after_date.count_events(), 1); // t4 combine methods var some_filtered_event = events_callect.start_before(223).start_after(100); - console.log(some_filtered_event.count_events() == 1); -} \ No newline at end of file + ok('sequence filters', some_filtered_event.count_events(), 1); +} +Collection_tests(); \ No newline at end of file diff --git a/Model.js b/Model.js index 2898e3f..a078f85 100644 --- a/Model.js +++ b/Model.js @@ -5,28 +5,29 @@ * @constructor * @param {Object} info Обект, описывающий общую информацию о мероприятии (место проведения, название, описание, временные рамки). */ -var Event = function (info) { - var def = { +var Event = new Model({ + 'default': { title: "event" - }; - - this.info = _.extend(def, info); - - if (!this.info.start_time || !this.info.end_time) { - throw "miss required fields"; + }, + 'constructor': function (info) { + this.update(info); + }, + 'errors': function (name) { + if (!this.get('start_time') || !this.get('end_time')) { + return "miss required fields"; + } + if (this.get('start_time') > this.get('end_time')) { + return "starat_time more then end_time"; + } + return null } - - if (this.info.start_time > this.info.end_time) { - throw "starat_time more then end_time"; - } - -}; +}); /** * Создает объект Event * - * @param {Number|Date} start Начало события - * @param {Number|Date} end Конец события + * @param {Number|Date} start_at Начало события + * @param {Number|Date} end_at Конец события * @param {String} [name="Событие"] Имя события * * @example @@ -47,7 +48,8 @@ function createNewEvent(start_at, end_at, name) { } -function Model_tests() { +function ModelTests() { var a = createNewEvent(222, 333, 'lol'); } +ModelTests(); \ No newline at end of file diff --git a/MyAbstractLayer.js b/MyAbstractLayer.js index c6536a2..1e2e94b 100644 --- a/MyAbstractLayer.js +++ b/MyAbstractLayer.js @@ -6,26 +6,398 @@ * To change this template use File | Settings | File Templates. */ -// Model interface -var Model = function (attrs) {}; +/** + * + * + * Model + * + * +*/ + +/** + * Метакласс для создания классов Model. + * + * @constructor + * @param {Object} ext Обект, расширяющийинформацию поведение класса или изменяющий его. + * @return {function constructor} Конструктор моделей + * + * @example + * var Event = Model({ + * 'default' : { + * 'title': "myEvent" + * }, + * 'constructor' : function (title, start_time) { + * if (title) { + * this.title = title; + * } + * this.start = start_time; + * }, + * errors: function (name) { + * if (name == "title" && this.get("title") === "WTF") { + * return "Can not create WTF event title"; + * } + * + * var error = ""; + * if (!this.get("title")) { + * error += "Required title. "; + * } + * if (this.get("title") && this.get("title") === "WTF") { + * error += "Can not create WTF event title. "; + * } + * return error; + * } + * }); + * + * var my_event = new Event("cool skool party", 123214121); + * + * my_event.get("title"); + * my_event.set("countPerson", 4); + */ +var Model = function (ext) { + var ModelConstructor = function () { // faced construct + if (this == window) { + throw new SyntaxError('WARNING: miss new'); + } + + this.__private_attrs = _.extend({}, this.default); + this.constructor.apply(this, arguments); + var errors = this.errors(); + if (errors) throw new SyntaxError("Invalid value in constructor: " + errors); + }; + ModelConstructor.prototype = _.extend({}, Model.prototype, ext); + + return ModelConstructor; +}; + +/** + * Коллекция, с значениями по умолчанию, для полей модели. + */ Model.prototype.default = {}; -Model.prototype.constructor = function () { throw new SyntaxError("no implement"); }; // void -Model.prototype.get = function () { throw new SyntaxError("no implement"); }; // true / false -Model.prototype.set = function () { throw new SyntaxError("no implement"); }; // true / false -Model.prototype.has = function () { throw new SyntaxError("no implement"); }; // true / false -Model.prototype.validate = function () { throw new SyntaxError("no implement"); }; // true / false -//Model.prototype.extend = Model2; -//Model.prototype.super = function () {}; // void -function TestModel() { - function ok(name, val1, val2) { - if (val1 !== val2) { - console.log(name + " [FAILED]"); - } else { - console.log(name + " [OK]"); +/** + * Конструктор, который будет вызван в моделе, порожденной конструктором Model (метоклассом). + * Количество параметров и поведение конструктора определяется индивидуально. + * + * @constructor + */ +Model.prototype.constructor = function () {}; + +/** + * Отдает текущее значение атрибута модели. + * + * @param name Имя атрибута + * @return {*} Значение + * + * @example + * alert(my_event.get('title')); + */ +Model.prototype.get = function (name) { + if (typeof name !== "string") { + throw new SyntaxError("Invalid parameter"); + } + + return this.__private_attrs[name]; +}; + +/** + * Устанавливает значение для модели. + * Если установлен флаг no_check_error становлен, проверка целостности не будет проводиться (функция error не будет вызвана). + * + * @param {String} name Имя атрибута + * @param {*} value Значение атрибута + * @param {Boolean} no_check_error Флаг, отвечающий за проверку ошибок + * @return {Boolean} Возвращает тру, если занчение установлено + * + * @example + * var my_event = new Event("cool skool party", 123214121); + * if (my_event.set("title", "WTF")) { + * alert('ok'); + * } else { + * alert('no set =('); + * } + */ +Model.prototype.set = function (name, value, no_check_error) { + if (typeof name !== "string") { + throw new SyntaxError("Invalid parameter"); + } + + var _back = this.__private_attrs[name]; + this.__private_attrs[name] = value; + + if (!no_check_error) { + var errors = this.errors(name); + if (errors) { + console.log('WARNING: rollback set method. validation error: ' + errors); + this.__private_attrs[name] = _back; // rollback; + return false; + } + } + return true; +}; + +/** + * Устанавливает занчение для нескольких параметров. + * Если установка хотя бы одного из значений вызовет ошибку, тогда состояние модели не будет изменено. + * + * @param {Object} dict Коллекция параметров + * @return {Boolean} Возвращает тру, если все занчения установлены + */ +Model.prototype.update = function (dict) { + var self = this; + var _back = {}; + + _.forEach(dict, function(val, key) { + self.set(key, val, true); + _back[key] = val; + }); + + var errors = this.errors(); + if (errors) { + console.log('WARNING: rollback update method. validation error: ' + errors); + _.update(this.__private_attrs, _back); // rollback; + return false; + } + + return true; +}; + +/** + * Проверяет установлено ли занчение атрибута. + * + * @param name Имя атрибута + * @return {Boolean} Возваращает тру, если данный атрибут присутствует в модели + */ +Model.prototype.has = function (name) { + if (typeof name !== "string") { + throw new SyntaxError("Invalid parameter"); + } + + return this.__private_attrs[name] != null; +}; + +/** + * Проверка на ошибки. + * Если параметр namе отсутствует, то будут проверены все атрибуты модели. + * + * @param name Имя атрибута проверяемого на наличие ошибок (не обязательный параметр) + * @return {String} Строкое представление текста ошибок, если они были замечены + */ +Model.prototype.errors = function (name) { return ""; }; + +/* + * + * Collection + * + */ + +/** + * Метакласс для создания коллекций моделей. + * + * @constructor + * @param {Object} ext Обект, расширяющийинформацию поведение коллекции или изменяющий его. + * @return {function constructor} Конструткор коллекций + * + * @example + * var EventsCollection = new Collection({ + * 'model': new Model({ + * 'default': { + * 'val0' : -1 + }, + 'constructor': function (v) { + if (v) this.set('val0', v); + } + }) + * }); + * + * + * var my_event_collection = new EventsCollection(); + * + * my_event_collection.add(new my_event_collection.model()); + * my_event_collection.add(new my_event_collection.model(1)); + * + * var my_event_collection_max_to_min_sorted = my_event_collection.sortBy(function (model) { return model.get('val0'); }); + * var my_event_collection_less_zero_filtered = my_event_collection.filter(function (model) { return model.get('val0') < 0; }); + * + * var M = new Model({ + * 'default': { + * 'a': 0, + * 'b': 100 + * }, + * 'constructor': function(a, b) { + * if (a) this.set('a', a); + * if (b) this.set('b', b); + * } + * }); + * + * var C = new Collection({ + * 'model': M, + * }); + * + * var c = new C([ + * new M(1), new M(2), new M(3), new M(4), new M(5), new M(6), new M(7) + * ]); + * + * c.each(function (m) { + * if (!m.has('b')) m.set('b', 100); + * }); + * + * c.map(function (m) { + * return m.get('a'); + * }); + * + * c.find(function (m) { + * return m.get('a') % 5 == 2; + * }); + * + * c.all(function (m) { + * return m.get('b') == 100; + * }); + * + * c.any(function (m) { + * return m.get('b') == 100; + * }); + * + * c.include(new M(1)); // --> false + * + * c.contains(new M(1)); // --> false + * + * c.max(function (m) { + * return m.get('a'); + * }); + * + * c.min(function (m) { + * return m.get('a'); + * }); + * + * c.toArray(); + * + * c.size(); + * + * c.first();c.head(); + * c.tail();c.last(); + * + * c.indexOf(new M(1)); // --> -1 + * c.lastIndexOf(new M(1)); // --> -1 + * + * c.groupBy(function (m) { + * return m.get('a') % 3; + * }); + * + * c.isEmpty(); + * + * c.foldl(function (memo, m) { + * return memo + m.get('a'); + * }, 0); + * + * c.foldr(function (memo, m) { + * return memo + m.get('a'); + * }, 0); + */ +var Collection = function (ext) { + var CollectionConstructor = function (models) { // faced construct + if (this == window) { + throw new SyntaxError('WARNING: miss new'); + } + + this.__private_models = []; + this.__private_constructor = arguments.callee; /* may be memory leek ? */ + + if (_.isArray(models)) { + for (var i = 0; i < models.length; i++) { + this.add(models[i]); + } } + this.initialize.apply(this, arguments); + }; + CollectionConstructor.prototype = _.extend({}, Collection.prototype, ext); + + return CollectionConstructor; +}; + +/** + * Функция вызываемая после созания соллекции. + */ +Collection.prototype.initialize = function (models) {}; + +/** + * Модель для членов коллекции + */ +Collection.prototype.model = new Model(); + +/** + * Взятие элемента по индексу из коллекции. + * + * @param index Индекс + * @return {*} Экземпляр модели (instanceof this.model) + */ +Collection.prototype.at = function(index){ return this.__private_models[index]; }; + +/** + * Срез коллекции + * + * @param from Начальный индекс + * @param to Конечный индекс + * @return {Array} Срез + */ +Collection.prototype.slice = function(from, to){ return this.__private_models.slice(from, to); }; + +/** + * Добавление элемента в модель + * + * @param {Object instanceof this.model} model_obj Элемент, который надо добавить в воллекцию + */ +Collection.prototype.add = function(model_obj){ + if (! model_obj instanceof this.model) { + throw new SyntaxError("can add object with strange model"); + } + this.__private_models.push(model_obj); +}; + +// Underscore methods that we want to implement on the Collection. +var methods = ['each', 'map', 'find', + 'all', 'any', + 'include', 'contains', + 'max', 'min', + 'toArray', + 'size', + 'first', 'head', 'tail', 'last', + 'indexOf', 'lastIndexOf', 'groupBy', + 'isEmpty', + 'foldr', 'foldl']; + +// Mix in each Underscore method as a proxy to `Collection#models`. +_.each(methods, function(method) { + Collection.prototype[method] = function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(this.__private_models); + return _[method].apply(_, args); + }; +}); + +// Underscore methods +var state_change_methods = ['filter', 'sortBy']; + +_.each(state_change_methods, function(method) { + Collection.prototype[method] = function() { + var args = Array.prototype.slice.call(arguments); + args.unshift(this.__private_models); + return new this.__private_constructor(_[method].apply(_, args)); + }; +}); + +/* + * TESTS + */ + +function ok(name, val1, val2) { + if (val1 !== val2) { + console.log(name + " [FAILED]"); + } else { + console.log(name + " [OK]"); } +} +function TestModel() { var M0 = new Model({ 'default': { 'val0' : -1 @@ -35,25 +407,12 @@ function TestModel() { this.set('val0', v0); } }, - 'validate': function () { - return this.get('val0') < 0; - } - }); - var M1 = new Model({ - 'default' : { - 'val1': 1, - 'val2': 2 - }, - 'constructor' : function (v0, v2) { - this.super(v0); - if (v2) { - this.set('val2', v2); + 'errors': function (name) { + if (this.get('val0') >= 0) { + return 'val0 can not be great than zero' } - }, - 'validate' : function() { - return this.get('val1') < 5 && this.get('val2') < 5; - }, - 'extend' : M0 + return null; + } }); var m = new M0(); @@ -67,19 +426,85 @@ function TestModel() { ok('Set and Get', m.get('val9'), -1); m = new M0(8888); - m.set('val0', 9999); - ok('Validator', m.get('val0'), -1); - - m = new M1(); - ok('Parent Default', m.get('val0'), -1); - - m = new M1(-10, -20); - ok('Parent Constructor 1', m.get('val0'), -10); - ok('Parent Constructor 2', m.get('val2'), -20); - - m = new M1(); - m.set('val0', 9); - m.set('val1', 9); - ok('Parent Get and Set', m.get('val0'), -1); - ok('Parent Get and Set', m.get('val1'), 1); -} \ No newline at end of file + ok('Validator return false', m.set('val0', 9999), false); + ok('Validator 1', m.get('val0'), -1); + ok('Validator return true', m.set('val0', -5), true); + ok('Validator 2', m.get('val0'), -5); + + var M1 = new Model({ + 'default': { + 'val0' : -2 + }, + 'constructor': function (v0, v1) { + if (v0) { + this.set('val0', v0); + } + if (v1) { + this.set('val1', v1); + } + } + }); + + m = new M1(4); + ok('Validate new Model no error', m.get('val0'), 4); + + m = new M1(); + ok('Validate new Model Default', m.get('val0'), -2); + + m = new M1(-10, -20); + ok('Validate new Model Constructor 1', m.get('val0'), -10); + ok('Validate new Model Constructor 2', m.get('val1'), -20); + + m = new M1(); + m.set('val0', 9); + m.set('val1', 9); + ok('Validate new Model Get and Set', m.get('val0'), 9); + ok('Validate new Model Get and Set', m.get('val1'), 9); +} +function TestCollection() { + var M0 = new Model({ + 'default': { + 'val0' : -1 + }, + 'constructor': function (v0) { + if (v0) { + this.set('val0', v0); + } + }, + 'errors': function (name) { + if (this.get('val0') >= 0) { + return 'val0 can not be great than zero' + } + return null; + } + }); + + var C0 = new Collection({ + 'model': M0 + }); + + var c = new C0(); + ok('Collection Model test', c.model, M0); + + var m = new M0(-10000); + + c = new C0([ + new M0(-10), new M0(-20), new M0(-30), new M0(-100), new M0(-200), new M0(-300), m + ]); + ok('Crate collection', c.size(), 7); + + var cf = c.filter(function (val) { + return val.get('val0') < -50; + }); + ok('Filter collection', cf.size(), 4); + ok('Initial collection', c.size(), 7); + + ok('Include in collection', c.include(m), true); + ok('Max in collection', c.min(function (val) { return val.get('val0'); }), m); + + // big ---> small + ok('SortBy collection', c.sortBy(function (val) { return (val.get('val0') % 3) * 10000 + val.get('val0') * -1; }).at(0).get('val0'), -20); + +} +TestModel(); +TestCollection(); diff --git a/backbone b/backbone new file mode 160000 index 0000000..35054da --- /dev/null +++ b/backbone @@ -0,0 +1 @@ +Subproject commit 35054dada664bcbeb13b382780ed550c3e7ed205 diff --git a/index.html b/index.html index b2c35b2..7784dc8 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,7 @@

hello

- + + From 2f8020f64cbd3374ff741960a63a54c1a1996df6 Mon Sep 17 00:00:00 2001 From: pahaz Date: Sun, 11 Nov 2012 18:44:43 +0600 Subject: [PATCH 3/7] fix some error --- MyAbstractLayer.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/MyAbstractLayer.js b/MyAbstractLayer.js index 1e2e94b..d994c1f 100644 --- a/MyAbstractLayer.js +++ b/MyAbstractLayer.js @@ -55,14 +55,16 @@ */ var Model = function (ext) { var ModelConstructor = function () { // faced construct - if (this == window) { + if (this === window) { throw new SyntaxError('WARNING: miss new'); } this.__private_attrs = _.extend({}, this.default); this.constructor.apply(this, arguments); var errors = this.errors(); - if (errors) throw new SyntaxError("Invalid value in constructor: " + errors); + if (errors) { + throw new SyntaxError("Invalid value in constructor: " + errors); + } }; ModelConstructor.prototype = _.extend({}, Model.prototype, ext); @@ -121,14 +123,14 @@ Model.prototype.set = function (name, value, no_check_error) { throw new SyntaxError("Invalid parameter"); } - var _back = this.__private_attrs[name]; + var back = this.__private_attrs[name]; this.__private_attrs[name] = value; if (!no_check_error) { var errors = this.errors(name); if (errors) { console.log('WARNING: rollback set method. validation error: ' + errors); - this.__private_attrs[name] = _back; // rollback; + this.__private_attrs[name] = back; // rollback; return false; } } @@ -144,17 +146,17 @@ Model.prototype.set = function (name, value, no_check_error) { */ Model.prototype.update = function (dict) { var self = this; - var _back = {}; + var back = {}; _.forEach(dict, function(val, key) { self.set(key, val, true); - _back[key] = val; + back[key] = val; }); var errors = this.errors(); if (errors) { console.log('WARNING: rollback update method. validation error: ' + errors); - _.update(this.__private_attrs, _back); // rollback; + _.update(this.__private_attrs, back); // rollback; return false; } @@ -172,7 +174,7 @@ Model.prototype.has = function (name) { throw new SyntaxError("Invalid parameter"); } - return this.__private_attrs[name] != null; + return this.__private_attrs[name] !== null; }; /** @@ -295,7 +297,7 @@ Model.prototype.errors = function (name) { return ""; }; */ var Collection = function (ext) { var CollectionConstructor = function (models) { // faced construct - if (this == window) { + if (this === window) { throw new SyntaxError('WARNING: miss new'); } @@ -409,7 +411,7 @@ function TestModel() { }, 'errors': function (name) { if (this.get('val0') >= 0) { - return 'val0 can not be great than zero' + return 'val0 can not be great than zero'; } return null; } @@ -473,7 +475,7 @@ function TestCollection() { }, 'errors': function (name) { if (this.get('val0') >= 0) { - return 'val0 can not be great than zero' + return 'val0 can not be great than zero'; } return null; } @@ -506,5 +508,6 @@ function TestCollection() { ok('SortBy collection', c.sortBy(function (val) { return (val.get('val0') % 3) * 10000 + val.get('val0') * -1; }).at(0).get('val0'), -20); } + TestModel(); TestCollection(); From a31e0a7e0b492b99f6dfff29c8037f4414e389f3 Mon Sep 17 00:00:00 2001 From: pahaz Date: Sun, 11 Nov 2012 20:27:23 +0600 Subject: [PATCH 4/7] from branch --- Collection.js | 11 ++++++++--- Model.js | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Collection.js b/Collection.js index ad1b9bd..0a790da 100644 --- a/Collection.js +++ b/Collection.js @@ -19,7 +19,7 @@ EventsCollection.prototype.start_before = function(date) { return event.get('start_time') < date; }); }; - + /** * Возвращает количество событий (Event) в коллекции. * @@ -28,7 +28,7 @@ EventsCollection.prototype.start_before = function(date) { EventsCollection.prototype.count_events = function() { return this.size(); }; - + /** * Отфильтровывает события, которые произошли после указанной даты. * @@ -40,6 +40,11 @@ EventsCollection.prototype.start_after = function(date) { }); }; +/** + * + * TESTS + * + */ function ok(name, val1, val2) { if (val1 !== val2) { @@ -55,7 +60,7 @@ function Collection_tests() { var a3 = createNewEvent(100, 500, 'lol2'); var events_data = [a1, a2, a3]; var events_callect = new EventsCollection(events_data); - + // t1 var events_before_date = events_callect.start_before(223); ok('events_before_date filter', events_before_date.size(), 2); diff --git a/Model.js b/Model.js index a078f85..9018ced 100644 --- a/Model.js +++ b/Model.js @@ -37,7 +37,7 @@ var Event = new Model({ * * @return {Object} */ -function createNewEvent(start_at, end_at, name) { +function createNewEvent(start_at, end_at, name, no_my) { var info = { start_time: start_at, end_time: end_at, From 861d093af2fd2645d5629b88b2146fbfd216f1d3 Mon Sep 17 00:00:00 2001 From: pahaz Date: Sun, 11 Nov 2012 20:44:16 +0600 Subject: [PATCH 5/7] JsHint fix and add my_events --- Collection.js | 25 +++++++++++++++++++------ Model.js | 13 ++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Collection.js b/Collection.js index 0a790da..7d77fdb 100644 --- a/Collection.js +++ b/Collection.js @@ -14,7 +14,7 @@ var EventsCollection = new Collection({ * * @return {EventsCollection} Новый объект EventsCollection. */ -EventsCollection.prototype.start_before = function(date) { +EventsCollection.prototype.start_before = function (date) { return this.filter(function (event) { return event.get('start_time') < date; }); @@ -25,7 +25,7 @@ EventsCollection.prototype.start_before = function(date) { * * @return {number} Кол-во событий. */ -EventsCollection.prototype.count_events = function() { +EventsCollection.prototype.count_events = function () { return this.size(); }; @@ -34,12 +34,23 @@ EventsCollection.prototype.count_events = function() { * * @return {EventsCollection} Новый объект EventsCollection. */ -EventsCollection.prototype.start_after = function(date) { +EventsCollection.prototype.start_after = function (date) { return this.filter(function (event) { return event.get('start_time') > date; }); }; +/** + * Фильтрует личные события. + * + * @return {EventsCollection} Новый объект EventsCollection. + */ +EventsCollection.prototype.my_events = function () { + return this.filter(function (event) { + return event.get('go'); + }); +}; + /** * * TESTS @@ -64,16 +75,18 @@ function Collection_tests() { // t1 var events_before_date = events_callect.start_before(223); ok('events_before_date filter', events_before_date.size(), 2); - + // t2 ok('events_before_date count_events()', events_before_date.count_events(), 2); - + // t3 var events_after_date = events_callect.start_after(222); ok('events_after_date filter count_events()', events_after_date.count_events(), 1); - + // t4 combine methods var some_filtered_event = events_callect.start_before(223).start_after(100); ok('sequence filters', some_filtered_event.count_events(), 1); + + ok('MY EVENTS filter', some_filtered_event.my_events().size(), 0); } Collection_tests(); \ No newline at end of file diff --git a/Model.js b/Model.js index 9018ced..b9deb75 100644 --- a/Model.js +++ b/Model.js @@ -7,7 +7,8 @@ */ var Event = new Model({ 'default': { - title: "event" + title: "event", + go: false }, 'constructor': function (info) { this.update(info); @@ -19,7 +20,7 @@ var Event = new Model({ if (this.get('start_time') > this.get('end_time')) { return "starat_time more then end_time"; } - return null + return null; } }); @@ -37,13 +38,16 @@ var Event = new Model({ * * @return {Object} */ -function createNewEvent(start_at, end_at, name, no_my) { +function createNewEvent(start_at, end_at, name, go) { var info = { start_time: start_at, end_time: end_at, name: name }; - + if (go) { + info.go = true; + } + return new Event(info); } @@ -52,4 +56,3 @@ function ModelTests() { var a = createNewEvent(222, 333, 'lol'); } ModelTests(); - \ No newline at end of file From 124974ca53663cee20e397c763dde35e69d6d394 Mon Sep 17 00:00:00 2001 From: pahaz Date: Mon, 12 Nov 2012 01:18:03 +0600 Subject: [PATCH 6/7] fix submodules --- underscore | 1 + 1 file changed, 1 insertion(+) create mode 160000 underscore diff --git a/underscore b/underscore new file mode 160000 index 0000000..df050be --- /dev/null +++ b/underscore @@ -0,0 +1 @@ +Subproject commit df050beae261ba5b1751b345d19f46e32a3dab69 From f5ac4c4bbcf79e5efb2f68299c0b312da91a75cc Mon Sep 17 00:00:00 2001 From: pahaz Date: Tue, 27 Nov 2012 03:21:00 +0600 Subject: [PATCH 7/7] fix bugs and logic --- Collection.js | 3 +-- Model.js | 37 ++++++++++++++++++++++++++++++++++--- MyAbstractLayer.js | 2 +- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/Collection.js b/Collection.js index 7d77fdb..411d633 100644 --- a/Collection.js +++ b/Collection.js @@ -66,7 +66,7 @@ function ok(name, val1, val2) { } function Collection_tests() { - var a1 = createNewEvent(222, 333, 'lol'); + var a1 = createNewEvent(222, 333, 'lol'); // invalid date var a2 = createNewEvent(223, 333, 'lol2'); var a3 = createNewEvent(100, 500, 'lol2'); var events_data = [a1, a2, a3]; @@ -89,4 +89,3 @@ function Collection_tests() { ok('MY EVENTS filter', some_filtered_event.my_events().size(), 0); } -Collection_tests(); \ No newline at end of file diff --git a/Model.js b/Model.js index b9deb75..632e21f 100644 --- a/Model.js +++ b/Model.js @@ -1,4 +1,23 @@  +function isValidDate(d) { + if ( Object.prototype.toString.call(d) === "[object Date]" ) { + // it is a date + if ( isNaN( d.getTime() ) ) { // d.valueOf() could also work + // date is not valid + return false; + } + else { + // date is valid + return true; + } + } + else { + // not a date + return false; + } +} + + /** * Модель для класса Event. * @@ -11,12 +30,25 @@ var Event = new Model({ go: false }, 'constructor': function (info) { + if (typeof info.start_time === 'string') { + info.start_time = new Date(Date.parse(info.start_time)); + } + if (typeof info.end_time === 'string') { + info.end_time = new Date(Date.parse(info.end_time)); + } + this.update(info); }, 'errors': function (name) { if (!this.get('start_time') || !this.get('end_time')) { return "miss required fields"; } + if (!isValidDate(this.get('start_time'))) { + return "invalid start_time value"; + } + if (!isValidDate(this.get('end_time'))) { + return "invalid end_time value"; + } if (this.get('start_time') > this.get('end_time')) { return "starat_time more then end_time"; } @@ -42,7 +74,7 @@ function createNewEvent(start_at, end_at, name, go) { var info = { start_time: start_at, end_time: end_at, - name: name + title: name }; if (go) { info.go = true; @@ -53,6 +85,5 @@ function createNewEvent(start_at, end_at, name, go) { function ModelTests() { - var a = createNewEvent(222, 333, 'lol'); + var a = createNewEvent(222, 333, 'lol'); // invalid date } -ModelTests(); diff --git a/MyAbstractLayer.js b/MyAbstractLayer.js index d994c1f..c9a716f 100644 --- a/MyAbstractLayer.js +++ b/MyAbstractLayer.js @@ -156,7 +156,7 @@ Model.prototype.update = function (dict) { var errors = this.errors(); if (errors) { console.log('WARNING: rollback update method. validation error: ' + errors); - _.update(this.__private_attrs, back); // rollback; + _.extend(this.__private_attrs, back); // rollback; return false; }