Pull to refresh

Comments 36

Спасибо за наводку — интересная вещь!
Ага. С вашими моделями получается миленький микро-фреймворк:)
… создавая сложные джаваскрипт-приложения, уже решает эту проблему каким-то определенным образом без фреймворков

Вопрос зачем? Зачем нужно создавать сложные js-приложения, не используя сторонние фреймворки? Поймите правильно, я против того, чтобы в каждое мало-мальское web-app пихали все подряд библиотеки и все это превращалось в что-то монструозное, но если приложение действительно достаточно сложное, то просто моделью вы все равно не обойдетесь, а следовательно лучше сразу использовать какую-нибудь хорошо известную связку.
Текущая ситуация с JS мне начинает напоминать ситуацию с PHP. Сделал свой велосипед — зачем? Да, я тоже не рад использованию везде и вся миллионов фреймворков. Особенно бесит, что сейчас уже «программируют на jQuery», а не на JS. Но ответ на Ваш вопрос «зачем?» — чтобы учиться. Хороший велосипед — перенять чужой опыт. Плохой — тоже перенять чужой опыт и не повторять ошибки.
Ответ «чтобы учиться» в этом случае самый правильный, ничего против не имею, только за.
Данное решение выглядит вполне хорошо, так почему этому не быть микрофреймворком. Лично мне данный проект внешне очень понравился, хочу попробовать в связке с KnockoutJS. Мне не хватает чего-то удобного для работы с моделью
Я соглашусь с вами, Сергей: без фреймворка действительно не обойтись. Миниатюрные же библиотеки в моих глазах хороши тем, что из них можно скомпоновать «свой» фреймворк, разработать свой способ, не принимая навязываемые другими фреймворками конвенции и ограничения. Если иметь ряд инструментов, каждый из которых исключительно хорош в своем узком деле, можно, построив из них систему, выйти за рамки и создать более качественный продукт. Как пример вспоминаются юниксы.

Я также очень настороженно отношусь к новым библиотекам и любой новый способ/подход, равно как и инструмент, конечно, должен пройти проверку боем и проверку временем. Model.js здесь не исключение.
Какой-то у вас сахар несладкий

var Note = new Model('Note', function () {
  this.attr('id!', 'number');
  this.attr('title', 'string', 'nonempty');
  this.attr('text', 'string');
});


Почему не так:

var Note = new Model({
    'id!' : 'number',
    'title': {
        type: 'string' // или String
        validator: 'non-empty'
    },
    'text': 'string'
});


note.bind(eventName, handler) «вешает» обработчик. Кстати говоря, повесить обработчик любого события можно не только на отдельную сущность, но и на все сущности класса (Note.bind).


Вообще круто. developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind Переопределять стандартный метод вместо того чтобы использовать общепринятые on или addEventListener

this.attr('title', 'string', 'nonempty', [ 'minLength', 6 ]);


Это разве синтаксический сахар?
Спасибо, вы задали интересный вопрос. Вообще, сахар-не-сахар — это все субъективно, но…

Смотрите, конфигурационную функцию конструктор моделей получает не просто так. Сейчас в ее контексте есть только метод this.attr(attrName, validator[, validator, …]), но в будущем появятся и другие методы, при помощи которых будет описываться модель. Например, this.validates(function () {…}), которая должна бы валидировать модель вцелом после проверки значений атрибутов. Есть и другие задумки.

Форма же записи объектом, которая вам больше по душе, несомненно хороша и более привычна. Но пример, который вы привели, с одной стороны немного избыточен, а с другой — не все отражает:

1. Тип нам нужен только чтобы его проверить, и поэтому ничего страшного нет в том, чтобы просто записать валидатор с названием типа, не делая особый синтаксический акцент на том, что это именно тип. Получается, вы немного усложнили, а не упростили.

2. Валидатор — не один — их может быть любое количество, как того может требовать логика нашего приложения. Каждый из валидаторов, когда проверяется значение, если возвращает ошибку, останавливает всю цепочку за ним.

this.attr('title', 'nonnull', 'string', 'nonempty', [ 'minLength', 6 ]);

В этом примере мы сперва проверяем не null ли, затем строка ли, затем не пуста ли и затем не меньше ли 6 символов в длину. Каждый их этих валидаторов может вернуть нам код своей ошибки и остановить на этом проверку конкретного значения. Код ошибки, который он возвратит, может что-то означать в нашем приложении. Даже то, что nonempty и [ 'minLength', 6 ] стоят рядом может иметь в нашем приложении специальный смысл (я имею ввиду, что в принципе nonempty можно и не указывать рядом с [ 'minLength', 6 ]).

Стоит также помнить и о том, что валидаторы могут принимать атрибуты, как в случае с minLength или in.

Если учесть изложенные нюансы и записать в форме объекта это описание, все становится очень похожим на тот пример, который вы критикуете.
var Note = new Model({
  'id!' : 'number',
  'title': [ 'nonnull', 'string', 'non-empty', [ 'minLength', 6 ] ],
  'text': 'string'
});

Но нам нужна эта конфигурационная функция,— выше я объяснил почему,— и поэтому форма объявления атрибута при помощи this.attr все же предпочтительней.

По поводу метода bind, то он стал стандартным только в JS 1.8.5 (в современных браузерах с марта 2011 года, начиная с ФФ4), в то время, как Model.js нацелена на JS 1.5 (c ноября 2000 года в абсолютном большинство современных браузеров). Но вы правы, в будущем этому нюансу стоит уделить больше внимания. А пока, в ближайших версиях, возможно, и стоит создать алиас на слово on, которое сделали популярным последние версии jQuery и Backbone.
С точки зрения «сахарности» можно иногда и усложнить, в угоду читаемости. Например:
var Note = new Model({
    'text':  {
        'name' : {
            type: 'string',
            validators: []
        }
    }
});


Несмотря на то, что тип это просто валидатор, можно для него сделать отдельное поле (естественно, как опцию, а не жесткое правило). Для валидаторов можно сделать два поля — validator и validators. Вот это и есть сахар. Причем обратывать эти поля можно одним и тем же кодом, то есть использовать их как синонимы (ну точне брать валидаторы из всех и объединять в массив).

Для аргументов валидаторов вы нашли плохое решение, потому что элементы массива равноправны, а названия валидатора и аргументы — нет. Понятно, что они нужны, но в итоге совершенно нечитаемо.

Насчет конфигурационной функции — не понятно, почему то же самое нельзя делать снаружи:
var Note = new Model('Note', function () {
}).attr('id!', 'number').validates(...)


По поводу метода bind, то он стал стандартным только в JS 1.8.5 (в современных браузерах с марта 2011 года, начиная с ФФ4), в то время, как Model.js нацелена на JS 1.5


Если вас так волнует поддержка старых браузеров, то весьма странно видеть использование __proto__,__defineGetter__ и __defineSetter__, которые вообще не являются стандартными.
Спасибо вам за мнение. Объективно, если усложнять, то это уже не «сахар». Я также не согласен с вами в том, что многоэтажные объекты легче читаются. Но, вообще, конечно, удобство — категория субъективная. Думаю, вы согласитесь, что тратить время ну пустые споры не имеет смысла.

Есть другой момент. Из ряда __proto__, __defineGetter__ и __defineSetter__ нестандартным в JS 1.5 есть только __proto__. Присмотритесь к коду. Использование __proto__ в 2 местах — это всего лишь перестраховка от кое-каких особенностей современных браузеров: если вообще удалить __proto__, смысл не пострадает, но вот в последних хромах (напр. версии 24.0.1312.70) прототипная цепочка почему-то не установится (слетит ровно 75 тестов).

Буду признателен, если вы сможете объяснить почему так происходит.
Для начала расскажите, чего вы хотите добиться такими конструкциями:
    cls.prototype = cls.__proto__ = private.Class.prototype;


Если cls это не функция, то установка prototype ничего не делает. Если cls это функция, то устанавливая __proto__ вы отрубаете Function.prototype. Отличный пост habrahabr.ru/post/140810/, еще dailyjs.com/2012/11/26/js101-proto/.

Если вы хотите, чтобы у конструктора были те же методы, что и у порождаемых им объектах (т. е. их прототипа), нужно скопировать их из прототипа в конструктор.
__defineGetter__ и __defineSetter__ тоже не относятся к стандарту (пруф и пруф), хотя и поддерживаются многими браузерами. Лучше определить shim для defineProperty (-ies) и использовать его.
Я про то что этих методов нет в стандарте ECMAScript. Там определены методы defineProperty и defineProperties. __defineGetter__ и __defineSetter__ — это проделки Мозиллы.
ведь чтобы ей стать «взрослой» библиотекой, нужно проделать еще немало работы
Что бы ей в конце-концов стать backbone.js надо действительно проделать немало работы
Никого не слушайте (имею ввиду критику без аргументов). Вы сделали несомненно очень крутую и полезную библиотеку. Это естественно, что у нее есть какие-то «детские болячки», и я надеюсь, что в скором времени вы ее вылечите и вырастите в легковесный и удобный рабочий инструмент. Удачи.
Спасибо на добром слове. Вы правильно поняли взятый вектор. Всегда приятно получить такой отзыв.
В качестве типа атрибута можно использовать другую модель?
Да, можно. Нужно создать валидатор, который проверит есть ли значение аттрибута сущностью другой модели. Сейчас нет какого-то красивого метода сущности для этой цели, поэтому нужно извернуться вот так:
var Author = new Model('Author', function () {…});
var Comment = new Model('Comment', function () {
  this.attr('author', function (author) {
    if (author.constructor.className != 'Author') return 'wrongtype'; 
  });
  this.attr('text', 'string');
});


Вообще, спасибо вам за идею — этот момент доработаем в ближайшей версии.

На ум пока пришла такая вот форма:
var Author = new Model('Author', function () {…});
var Comment = new Model('Comment', function () {
  this.attr('author', 'Author');
  this.attr('text', 'string');
});
А наследовать модели друг от друга нельзя?
Я не уверен, что это действительно нужно с вашей библиотекой, но c Backbone я часто наследуюсь, и это удобно.
В целом все довольно просто, но вешать глобальный обработчик на класс, что бы изменять инстанс по дефолту крайне витиеватое решение. Не очень приятно наследование через callback. Что бы добавить свойств, хотелось бы иметь возможность их явно указать, например, их можно передавать объектом.
В Backbone прекрасная механика наследования, за счет этого, что бы задать дефолтные значения, достаточно передать свойство default. Тут же появляется возможность переопределять конструктор и функцию инициализации. Расширить валидацию свойств не проблема.
Так же для остлеживания создания моделей на глобальном уровне, достаточно создать коллекцию, что даёт более обширные возможности с точки зрения выборок. Но обучение дело золотое, не спорю.
Спасибо. Вы знаете, в будущих версиях синтаксис объявления атрибутов наверняка будет доработан. Вы ведь правы: сейчас нужно много движений, чтобы установить значение по умолчанию, да и функция инициализации и вправду не помешала бы.

По поводу выборок также есть задумки. В текущей же версии вопрос регистра сущностей и методов класса моделей, которые как-то бы с этим регистром работали, умышленно обойден. Но не могу не согласиться, это и вправду нужная функциональность.
Пожалуйста, не пишите так:
var note = this;
  return $.ajax({
    type: 'PUT',
    url: '/notes/'+this.data.id,
    data: this.data(),
    dataType: 'json'
  }).done(function (json) {
    note._persist();
  });

у jq есть замечательный параметр context
return $.ajax({
    type: 'PUT',
    url: '/notes/'+note.data.id,
    data: note.data(),
    dataType: 'json',
    context: this,
  }).done(function (json) {
    this._persist();
  });


Биндить скоупы нужно всегда, для этого в jq есть метод $.proxy(fn,scope,args). хорошая статья про скоупы
А чем не угодил нативный fn.apply( scope, args )?
_не во всех_ браузерах есть, а any_lib.bind предоставит фаллбек в случает IE<9 и других устаревших браузеров
На мой вкус не хватает коллекций и сериализации в json. А так достаточно неплохо получилось.
Спасибо. Коллекций, правда, пока нет. Сериализация же подразумевается при помощи внешних средств, ведь JSON.stringify пока не везде есть (только начиная с JS 1.7), чтобы его широко использовать.
Нитпик: «State of the art» — это высшее достижение ремесла на текущий момент. А не «незаконченный набросок», в каком качестве эта фраза, по всей видимости, используется в этом тексте. В остальном — кул!
Спасибо! Вы, правда, не правы. Не «незаконченный набросок», а все же «промежуточный, но уверенно работающий результат», что не мешает ему быть высшим достижением ремесла на текущий момент.
Sign up to leave a comment.

Articles